Как реализовать шаблон посетителя в F #, не ограничиваясь одним файлом?

В следующем примере кода демонстрируется реализация шаблона посетителя в F #.

module VisitorPattern

    type IVisitor =
        abstract Visit : ObjectA -> unit
        abstract Visit : ObjectB -> unit

    and IVisitable =
        abstract InvokeVisit : IVisitor -> unit

    and ObjectA =
        interface IVisitable with
            member this.InvokeVisit (visitor: IVisitor) =
                visitor.Visit(this)

    and ObjectB =
        interface IVisitable with
            member this.InvokeVisit (visitor: IVisitor) =
                visitor.Visit(this)

    type MyVisitor =
        member this.Visit (a : ObjectA) =
            printfn "Visited object A"

        member this.Visit (b : ObjectB) =
            printfn "Visited object B"

Это нормально компилируется, но мы ограничены тем, что все типы, реализующие IVisitable, в одном файле из-за использования ключевого слова and. Это ключевое слово кажется необходимым для разрешения взаимных ссылок на типы.

Есть ли способ реализовать этот шаблон таким образом, чтобы мы не ограничивались одним файлом?

(Я не спрашиваю мнения о том, следует ли вам использовать этот шаблон в F #)

РЕДАКТИРОВАТЬ: я задаю этот вопрос, потому что шаблон посетителя актуален при взаимодействии с кодом C #.


person Chiel ten Brinke    schedule 25.06.2019    source источник
comment
Вы используете функциональный язык программирования, так почему бы не использовать версию паттерна Visitor для функционального программирования: functions? Т.е., member this.InvokeVisit (visit : ObjectA -> unit) = visit this.   -  person rmunn    schedule 25.06.2019
comment
См. Также voxxed.com/2016/05 / gang-four-patterns -function-light-part-4 о том, как создать шаблон Visitor в Scala; хотя я не знаю Scala и только бегло просмотрел эту статью, ее функции должны быть достаточно похожи на те, что в F #, чтобы вы могли преобразовать статью в F # без слишком большого несоответствия импеданса.   -  person rmunn    schedule 25.06.2019
comment
Хотя вы не спрашиваете мнения о том, следует ли вам использовать этот шаблон, я не думаю, что можно реально ответить на этот вопрос без этого.   -  person VoronoiPotato    schedule 25.06.2019
comment
@VoronoiPotato обновил ответ, чтобы объяснить вам причину. Также я не согласен с вашим утверждением об отсутствии ответа на этот вопрос. Если вы уверены, что ответ отрицательный, опубликуйте ответ с объяснением причины.   -  person Chiel ten Brinke    schedule 25.06.2019
comment
Вы можете написать любую конструкцию на любом языке, однако наступает момент, когда это становится более сложным, чем просто использование другого подхода. Написание шаблона посетителя на F # похоже на добавление педалей к роликам. Естественно, возникает вопрос, почему вы думаете, что они нужны, взаимодействуют или нет, и для чего вы могли бы использовать их.   -  person VoronoiPotato    schedule 25.06.2019
comment
stackoverflow.com/questions/694651/ Вот хороший пример. и вот способ без особых проблем использовать посетителя C #. bugsquash.blogspot.com/2012/03/   -  person VoronoiPotato    schedule 25.06.2019
comment
@VoronoiPotato спасибо, похоже, это полезная ссылка! Отвечая на ваш предыдущий комментарий: если шаблон действительно оказывается сложным для реализации (что я не могу оценить и неявно задается как вопрос в OP), тогда действительно имеет смысл поискать альтернативы. Будучи новичком в F #, я хотел бы знать, в какой степени это так.   -  person Chiel ten Brinke    schedule 25.06.2019
comment
Лучше спросить о реальной проблеме, которую вы пытаетесь решить, а не о том, как исправить это решение. Последнее считается проблемой XY, что часто приводит к нечетким вопросам с большим количеством комментариев, пытающихся выяснить, что проблема на самом деле есть.   -  person glennsl    schedule 25.06.2019
comment
@rmunn Конкретное ограничение, о котором задается вопрос (мы ограничены тем, что все типы, реализующие IVisitable в одном файле, из-за использования ключевого слова and) не существует в Scala никаким образом.   -  person Alexey Romanov    schedule 25.06.2019
comment
В F # мы хотим, чтобы типы зависели от типов, объявленных до него. Технически есть способ обойти это, но это тщательно охраняемый секрет, и я буду защищать его своей могилой: P. У нас есть прямые решения для многих вещей, для которых есть шаблоны C #, поэтому лучше направить пользователей к этим решениям, чтобы они выучили язык. Поощрение их к внедрению шаблонов C # в среде, где они больше не нужны, может принести больше вреда, чем помощи.   -  person VoronoiPotato    schedule 25.06.2019
comment
Это namespace rec NamespaceNameHere docs.microsoft.com/en-us/dotnet/ fsharp / language-reference /, но он вам почти наверняка не понадобится! Если вы включите его, все станет немного труднее читать, и очень трудно вернуться назад. По моему опыту, лучше просто притвориться, что его не существует, потому что это уводит вас от счастливого пути, особенно если вы не знакомы с F #.   -  person VoronoiPotato    schedule 25.06.2019
comment
Новичкам нравится идея, что они могут писать объектно-ориентированный код на F #, и они уверены, что это здорово, но это окажет языку медвежью услугу, если вы никогда не исследуете за пределами этого объектно-ориентированного пространства. Особенно, если вы начнете писать выкройки для решения проблем, которых больше нет :).   -  person VoronoiPotato    schedule 25.06.2019


Ответы (2)


Сопоставление с образцом должно достигать той же цели с небольшой долей сложности и накладных расходов. По моему личному опыту, это лучший способ реализовать шаблон посетителя в F #.

type Visitor = A of int | B of int

match a with 
| A x -> 1 * x
| B x -> 2 * x

то для некоторого возможного С #

private static void Main()
{
    Visitor a = Visitor.A(7)
    switch(a){
        case Visitor.A x:
            x.Item * 1;
            break;
        case Visitor.B x:
            x.Item * 2;
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
}
person VoronoiPotato    schedule 25.06.2019
comment
Если вас беспокоит исчерпывающее сопоставление, вы всегда можете добавить метод сопоставления, который принимает лямбду для каждого случая. - person VoronoiPotato; 25.06.2019

Если вам действительно нужно использовать шаблоны OO для взаимодействия с C #, я считаю, что лучший способ разделить типы - использовать универсальные шаблоны:

module VisitorPattern =

    type IVisitor<'T> =
        abstract Visit : 'T -> unit

    type IVisitable<'T> =
        abstract InvokeVisit : IVisitor<'T> -> unit

module Visitors =

    open VisitorPattern

    type ObjectA () =
        interface IVisitable<ObjectA> with
            member this.InvokeVisit (visitor : IVisitor<ObjectA>) =
                visitor.Visit this

    type ObjectB () =
        interface IVisitable<ObjectB> with
            member this.InvokeVisit (visitor : IVisitor<ObjectB>) =
                visitor.Visit this
person dumetrulo    schedule 27.06.2019
comment
Я не понимаю, как это сработает. Как бы вы реализовали класс MyVisitor из моего примера кода в терминах этого решения? - person Chiel ten Brinke; 27.06.2019