我可以重载操作数是函数的运算符吗?

时间:2018-04-16 10:18:04

标签: f#

是否可以在F#中重载操作符,其中操作数是函数?

例如(使用Chessie):

module AsyncTrialOperators =
  type Ops=
    /// Left-to-right Kleisli composition
    static member (>=>) (f:'a -> AsyncResult<'b,_>, g:'b -> AsyncResult<'c,_>) =
      fun x ->
        asyncTrial {
        let! y = f x
        let! z = g y
        return z }

    /// Left-to-right Kleisli composition
    static member (>=>) (f:'a -> AsyncResult<'b,_>, g:'b -> Result<'c,_>) =
      fun x ->
        asyncTrial {
        let! y = f x
        let! z = g y
        return z }

  // Example usage (doesn't compile)
  let f x = ok x
  let g x = fail x

  let z = f >=> g

给出以下错误: FS0043 Expecting a type supporting the operator '>=>' but given a function type. You may be missing an argument to a function.

1 个答案:

答案 0 :(得分:6)

它不起作用,因为Ops不是参数的一部分。

您可以使用将其中一个参数移动到lambda的技巧,然后使用vacant参数接收容器类型的伪参数:

#r @"c:\packages\chessie.0.6.0\lib\net40\Chessie.dll"

open Chessie
open Chessie.ErrorHandling

type Ops=

    /// Left-to-right Kleisli composition
    static member (>=>) (_:Ops, g:'b -> Result<'c,_>) =
        fun (f:'a -> AsyncResult<'b,_>) x ->
            asyncTrial {
            let! y = f x
            let! z = g y
            return z }

/// Left-to-right Kleisli composition
    static member (>=>) (_:Ops, g:'b -> AsyncResult<'c,_>) =
        fun (f:'a -> AsyncResult<'b,_>) x ->
            asyncTrial {
            let! y = f x
            let! z = g y
            return z }

然后,您可以在全球范围内重新定义>=>

let inline (>=>) x y = (Unchecked.defaultof<Ops> >=> y) x

但现在你会发现传递了错误的参数,f必须是AsyncResult<_,_>类型,因为我不熟悉棋子我将使用虚拟函数:

 let f x = Unchecked.defaultof<AsyncResult<int,int>>
 let g x = fail x

现在再详细一点,为避免价值限制,您必须在双方添加x

let z x = (f >=> g) x

这样可行,但我强烈建议您不要使用此设计。

如果你问我,特别是在全球范围内,超载必须遵循一些规则才能保持一致。这种Ad-hoc重载设计a-la C#不能很好地添加更多东西,很快就会遇到类型推断不知道你想做什么的情况,甚至是你的读者代码。

如果您想使用通用(且表现良好)>=>,请查看F#+

中定义的那个