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
? почему это нужно "обрабатывать"? что является результатом обработки? из вашего кода похоже, что результат "обработки" является еще одним "обработчиком", но так ли это? И так далее.
Если вы сможете уточнить свой домен лучше, я уверен, что мы сможем найти лучшее решение.