【发布时间】:2017-11-30 14:12:34
【问题描述】:
我已经四处寻找答案,但已经用尽了所有尝试。
我想覆盖一个已经在项目中定义的自定义运算符,经典的 compose >=> 运算符,这样如果它与我的类类型一起使用,它将使用它的静态运算符重载,但每次我使用该类时使用 >=> 运算符,它给我的错误是我的类与全局运算符定义不兼容。帮助解释:
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# 中研究了使用虚拟方法使用重载运算符覆盖的声明方式,但这似乎不起作用。
所有这些黑客(为了更好的词)是在使用 Node 类存储这些处理程序以构建到 Node 树中的同时维护 compose 运算符格式。子列表是可选的这一事实意味着,尽管我可以使用 (key, composedHandlers) / (key, composedHandlers, ChildList) 的元组重载构造函数,但这太丑陋且无法接受,即:
let node1 = Node ("key1", handle1 >=> handle2 , [
Node ("key2",handle3 >=> handle4, [
Node ("key3",handle5)
])
])
我可以将类静态运算符更改为 => / ==> 之类的东西,但它会导致混淆何时使用 >=> 或使用 =>,要求用户弄清楚是否处理程序正在馈送到一个节点,或者它们只是将两个处理程序组合成一个。
如果静态解析的类型条件没有锁定到仅核心,我可以执行以下操作...但不允许:
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
【问题讨论】:
标签: types f# operator-overloading overriding function-composition