Types F#:使用静态方法重载或类型条件运算符重写全局运算符
我已经到处寻找答案,但已经用尽了所有的努力 我想覆盖一个已经在项目中定义的自定义运算符,即经典的composeTypes F#:使用静态方法重载或类型条件运算符重写全局运算符,types,f#,operator-overloading,overriding,compose,Types,F#,Operator Overloading,Overriding,Compose,我已经到处寻找答案,但已经用尽了所有的努力 我想覆盖一个已经在项目中定义的自定义运算符,即经典的compose=>运算符,这样,如果它与我的类类型一起使用,它将使用其静态运算符重载,但每次我使用带有=>运算符的类时,它给了我一个错误,我的类与全局运算符定义不兼容。要帮助解释: type Handler = context -> context option // context just placeholder for type let (>=>) (a:Handler) (
=>
运算符,这样,如果它与我的类类型一起使用,它将使用其静态运算符重载,但每次我使用带有=>
运算符的类时,它给了我一个错误,我的类与全局运算符定义不兼容。要帮助解释:
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操作符格式。子列表是可选的这一事实意味着,尽管我可以使用(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
键入ComposeExtension=ComposeExtension和
静态成员(?=>)a b=(?我认为没有一种方法可以完全满足您的要求,但是如果您的目标是生成一个好看的DSL,那么您可以采用稍微不同的方法:将“just node”案例和“node with handlers applied”案例包装到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
这在语法上起作用,并且与您的原始签名相匹配,但我必须说,这在我看来有点荒谬,这是因为我不太明白您的最终目标是什么:什么是节点
?为什么需要“处理”?处理的结果是什么?从您的代码来看,它看起来像是“处理”的结果是另一个“处理者”,但这是正确的吗?等等
如果你能更好地澄清你的领域,我相信我们能想出一个更好的解决方案。我对的回答说明了如何重新连接全球运营商
如果你发布一个最小的复制,我可以告诉你如何将它应用到你的案例中
话虽如此,我还是建议使用已经包含运算符=>
的函数的函数,要使它与类一起工作,您所要做的就是定义Bind
和Return
,如下所示:
static member Return a = ...
static member Bind (x, f) = ...
同样,如果你给我看你的代码,我可以提供更多的细节。你在上一篇文章中的解决方案正是我想要的,非常感谢!!!我能够处理所有这三种情况!!感谢你的帮助,我试图避免用DU包装,因为b
第二个变量也可以是节点列表
,这样所有子变量链中的OUNT处理程序也需要包装,这只会使整个事情变得混乱。请注意,节点的上下文有点模糊,因此表示歉意。