F#: Переопределение глобального оператора с помощью статической перегрузки метода или условного оператора типа

Я искал ответ на этот вопрос, но исчерпал все попытки.

Я хочу переопределить пользовательский оператор, уже определенный в проекте, классический состав >=> оператор, такой, что если он используется с моим типом класса, он будет использовать вместо этого свою статическую перегрузку оператора, но каждый раз, когда я использую класс с >=> оператор, это дает мне ошибку, что мой класс не совместим с определением глобального оператора. чтобы помочь объяснить:

type Handler = context -> context option // context just placeholder for type

let (>=>) (a:Handler) (b:Handler) = fun ctx -> match a ctx with | Some u -> b u | None

type Node(key) =
...
member static (>=>) (p:Node,h:Handler) = ...
member static (>=>) (p:Node,pl Node list) = ...

Это так, что я могу написать код, который может обернуть составные обработчики как

// val Node = Node (overloaded >=>) Handler (overloaded >=>) Handler (overloaded >=>) [ ... ]
let node = Node "key1" >=> handler1 >=> hander2 >=> [
                                Node "key2" >=> handler3
// val Handler = Handler (global >=>) Handler
let handler3 = handler1 >=> handler2 
                                ]

Но, к сожалению, перегрузки статического метода в классе не могут переопределить глобальный оператор и иметь приоритет... есть ли что-то, что я могу сделать, чтобы переопределить глобальный оператор, чтобы заставить эту работу!? ... Я знаю, что могу изменить свой дескриптор с дополнения типа на полную реализацию класса, удалить глобальный оператор и использовать только статические переопределения для двух классов, но для интеграции с существующей платформой, на которую я смотрю, должна быть функция этого формата...

Я думал о возможной перегрузке FSharpFunc> статическим членом (>=>), но в F# это должно быть объявлено в исходном месте объявления, которое запрещено.

Я искал в C# заявленный способ переопределения с помощью оператора перегрузки, используя виртуальные методы, но, похоже, это не сработало.

Весь этот взлом (из-за отсутствия лучшего слова) заключается в поддержании формата оператора compose при использовании класса Node для хранения этих обработчиков для встраивания в дерево Node. Тот факт, что дочерние списки являются необязательными, означает, что, хотя я мог бы перегружать конструктор, используя кортежи (key, composedHandlers) / (key, composedHandlers, ChildList), это было бы слишком уродливо и завернуто, чтобы быть принятым, то есть:

let node1 = Node ("key1", handle1 >=> handle2 , [
                                        Node ("key2",handle3 >=> handle4, [
                                                                    Node ("key3",handle5)
                                                                    ])
                                                ])

Я мог бы изменить статический оператор класса на что-то вроде => / ==> но тогда это вызывает путаницу относительно того, когда использовать >=> или использовать =>слишком много просить пользователя выяснить, поступают ли обработчики в узел или они просто объединяют два обработчика в один.

Если статически разрешенные условия типа не были привязаны только к Core, я мог бы сделать что-то вроде следующего... но не разрешил:

let (>=>) (a:^T) (b:Handler)
     when ^T : Handler   = ...
     when ^T : Node      = ...
     when ^T : Node list = ...

Проблема сводится к тому, как сделать условный тип оператора условным или как сделать перегруженный оператор класса переопределенным глобальным оператором, чтобы я мог использовать оба вместе в зависимости от типов инфиксов.

---РЕДАКТИРОВАТЬ---

Связанный пост @Gustavo был именно тем, что я искал:

type ComposeExtension = ComposeExtension with
    static member        (?<-) (ComposeExtension, (a:PathNode) , (b:HttpHandler)) = a.AddHandler b
    static member        (?<-) (ComposeExtension, (a:PathNode) , (b:PathNode list)) = a.AddChildPaths b
    static member inline (?<-) (ComposeExtension, a , b) = a >=> b
let inline (>=>) a b = (?<-) ComposeExtension a b

2 ответа

Решение

Мой ответ на этот вопрос показывает, как перемонтировать глобальный оператор.

Если вы опубликуете минимальную репродукцию, я покажу вам, как применить ее к вашему делу.

Сказав это, я бы рекомендовал использовать FSharpPlus, который содержит уже лучшее глобальное определение оператора >=> и все, что вам нужно сделать, чтобы заставить его работать с вашим классом, это определить Bind а также Return, как это:

static member Return a = ...
static member Bind  (x, f) = ...

Опять же, если вы покажете мне свой код, я могу добавить больше деталей.

Я не думаю, что есть способ сделать именно то , что вы хотите, но если ваша цель состоит в том, чтобы создать красивый DSL, вы можете пойти немного по другому пути: оберните и случай "просто узел", и узел "с обработчики применили "case к DU", затем определите оператор на этом DU:

type Handler = context -> context option
type Node = Node of key:string
type Composable = CNode of Node | CHandled of Handler

let node key = CNode (Node key)

let inline (>=>) (a:Composable) (b:Handler) = 
    match a with
    | CNode n -> ...
    | CHandled h -> ...

// Usage:
let handler1 = fun ctx -> ...
let handler2 = fun ctx -> ...

let a = node "abc" >=> handler1 >=> handler2

Это работает синтаксически и соответствует вашим оригинальным подписям, но я должен сказать, что это выглядит немного бессмысленным для меня, и это потому, что я не совсем понимаю, какова ваша конечная цель: что является Node? почему это нужно "обрабатывать"? что является результатом обработки? из вашего кода похоже, что результат "обработки" является еще одним "обработчиком", но так ли это? И так далее.

Если вы сможете уточнить свой домен лучше, я уверен, что мы сможем найти лучшее решение.

Другие вопросы по тегам