【问题标题】:Overloading operators in type extensions类型扩展中的重载运算符
【发布时间】:2014-10-27 19:49:33
【问题描述】:

好的,所以我基本上是在尝试将绑定运算符添加到选项类型中,似乎我尝试的所有内容都有一些不明显的警告,阻止我这样做。我怀疑这与 .NET 类型系统的限制有关,并且可能与无法在用户代码中实现类型类的原因相同。

无论如何,我已经尝试了几件事。

首先,我尝试了以下方法

let (>>=) m f = ???

意识到我想根据m 的类型做不同的事情。 F# 不允许函数重载,但 .NET 允许方法重载,所以尝试第二个:

type Mon<'a> =
    static member Bind(m : Option<'a>, f : ('a -> Option<'b>)) =
        match m with
        | None -> None
        | Some x -> f x
    static member Bind(m : List<'a>, f : ('a -> List<'b>)) = 
        List.map f m |> List.concat

let (>>=) m f = Mon.Bind(m, f)

没有骰子。无法根据先前给定的类型信息选择唯一的重载。添加类型注释。

我已经尝试让操作符内联,但仍然报同样的错误。

然后我想我可以让&gt;&gt;= 运算符成为一个类型的成员。我很确定这会起作用,但我认为我不能在现有类型上破解它。您可以使用 type Option&lt;'a&gt; with 扩展现有类型,但不能将运算符作为扩展。

这是我对这段代码的最后一次尝试:

type Option<'a> with
    static member (>>=) (m : Option<'a>, f : ('a -> Option<'b>)) =
        match m with
        | None -> None
        | Some x -> f x

“扩展成员不能提供运算符重载。请考虑将运算符定义为类型定义的一部分。”太棒了。

我还有其他选择吗?我可以在单独的模块中为不同的 monad 定义单独的函数,但如果你想在同一个文件中使用多个版本,这听起来很糟糕。

【问题讨论】:

  • @JohnPalmer 哦,对不起。关键是 Bind 方法有多个重载。然后它抛出一个错误。我会在问题中解决它。
  • 我认为您的问题是您希望类型构造函数为一般 (>>=) 提供合理的类型(记住 Monad 有种类 * -> *) - 内联的静态约束因此,函数也无济于事 - 所以不,你不走运。
  • 我有时将这些运算符包装在一个内部模块中(通常只命名为运算符) - 所以如果我想要这些,我可以 open MyMonad.Operators - 或者必须 MyMonad.Operators.&gt;&gt;= :(
  • 如果你真的很绝望,可以随时修改Option.fs
  • @JohnPalmer +1 - 但为什么呢? opt &gt;&gt;= (fun x -&gt; ... 在我看来并不比 opt |&gt; Option.bind (fun x -&gt; ... 好多少

标签: f# operator-overloading inline optional


【解决方案1】:

您可以将 .NET 重载解决方案与内联/静态约束结合起来以获得所需的行为。

这是一步一步的explanation,这是一个针对您的特定场景的小型工作示例:

type MonadBind = MonadBind with
    static member (?<-) (MonadBind, m:Option<'a>, _:Option<'b>) = 
        fun (f:_->Option<'b>) ->
            match m with
            | None -> None
            | Some x -> f x
    static member (?<-) (MonadBind, m:List<'a>, _:List<'b>) = 
        fun (f:_->List<'b>) ->
            List.map f m |> List.concat

let inline (>>=) m f : 'R = ( (?<-) MonadBind m Unchecked.defaultof<'R>) f

[2; 1] >>= (fun x -> [string x; string (x+2)]) // List<string> = ["2"; "4"; "1"; "3"]
Some 2 >>= (fun x -> Some (string x))          // Option<string> = Some "2"

您也可以“手动”指定约束,但使用运算符时会自动推断。

我们在FsControl 中使用这种技术的改进(没有运算符)来定义 Monad、Functor、Arrow 和其他抽象。

另请注意,您可以直接使用Option.bindList.collect 来定义两个绑定。

【讨论】:

  • 不错!我应该使用 FsControl 还是 fsharp-typeclasses?
  • @LukaHorvat fsharptypeclasses 是一个初始实验/演示项目。改用 FsControl,它旨在用作生产 F# 项目的核心库。
【解决方案2】:

为什么需要(重新定义)“绑定”?对于初学者来说, Option.bind 已经定义好了。

您可以使用它来定义“计算表达式构建器”(F# 名称为一元“do”语法糖)。 见previous answer.

【讨论】:

    猜你喜欢
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-13
    • 2015-11-19
    • 1970-01-01
    • 2010-09-15
    相关资源
    最近更新 更多