F#:使用静态方法重载覆盖全局运算符或键入条件运算符

时间:2017-06-27 13:35:33

标签: types f# operator-overloading override compose

我已经远远地看了解答案,但已经用尽所有的尝试。

我想覆盖已在项目中定义的自定义运算符,即经典的compose select t3.id,t3.ath_id,t3.brand_id,t3.time,t3.date from times1 t3 inner join ( select t1.ath_id,t1.brand_id,t1.date,t1.time,count(*) as rnk from times1 t1 inner join times1 t2 on t1.ath_id=t2.ath_id and t1.brand_id=t2.brand_id and t1.date=t2.date and t1.time >= t2.time where t1.ath_id=4298584 group by t1.ath_id,t1.brand_id,t1.date,t1.time ) t4 on t3.ath_id=t4.ath_id and t3.brand_id=t4.brand_id and t3.date=t4.date and t3.time = t4.time and t4.rnk=1 ; 运算符,这样如果它与我的类类型一起使用,它将使用其静态运算符重载,但每次使用时使用>=>运算符的类,它给出了我的类与全局运算符定义不兼容的错误。帮助解释:

>=>

这样我可以编写可以包装像

这样的组合处理程序的代码
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) = ...

但不幸的是,类上的静态方法重载不能覆盖全局运算符并优先...有什么我可以做的事情来覆盖全局运算符来使其工作!? ...我知道我可以将我的Handle从类型扩充更改为完整类实现,删除全局运算符并仅对这两个类使用静态覆盖,但要与我正在查看的现有框架集成,需要是一个函数这种格式...

我在考虑过度加载FSharpFunc>使用静态成员(> =>)但在f#中,这需要在原始声明位置声明,这是不受限制的。

我在c#中声称要使用虚拟方法覆盖重载运算符,但这似乎不起作用。

所有这些Hacking(想要更好的词)是使用Node类来维护compose运算符格式,以存储这些处理程序以构建到Node树中。子列表是可选的这一事实意味着虽然我可以使用// 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 ] / (key, composedHandlers)的元组重载构造函数,但这将太丑陋并且被包裹以被接受,即:

(key, composedHandlers, ChildList)

我可以将类静态运算符更改为类似let node1 = Node ("key1", handle1 >=> handle2 , [ Node ("key2",handle3 >=> handle4, [ Node ("key3",handle5) ]) ]) / =>的内容,但这会导致混淆何时使用==>或使用>=>,这太过分了要求用户弄清楚处理程序是否正在进入某个节点,或者他们是否纯粹将两个处理程序合并为一个。

如果静态解析的类型条件没有锁定到Core,我可以执行以下操作......但不允许:

=>

问题归结为:如何使操作符类型成为条件或如何使重载类操作符覆盖全局操作符,以便我可以根据中缀类型一起使用它们。

--- --- EDIT

@Gustavo链接的帖子正是我想要的:

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

2 个答案:

答案 0 :(得分:3)

我对this question的回答显示了如何重新连接全局运营商。

如果你发布了一个最小的repro,我可以告诉你如何将它应用到你的案例中。

话虽如此,我建议您使用已包含>=>运算符Bind的{​​{3}},并且要使其与您的类一起工作所需要做的就是定义Returnstatic member Return a = ... static member Bind (x, f) = ... ,如下所示:

parameters.yml

同样,如果您向我展示您的代码,我可以提供更多详细信息。

答案 1 :(得分:0)

我认为没有办法完全 你想要什么,但如果你的目标是生产一个漂亮的DSL,你可以采用一个稍微不同的路线:将“只是节点”的情况和“带有处理程序的节点应用”的情况转换为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是什么?为什么需要“处理”?处理的结果是什么?从你的代码看来,“处理”的结果是另一个“处理程序”,但这是正确的吗?等等。

如果您能更好地澄清您的域名,我相信我们可以提出更好的解决方案。