好的,我基本上是在尝试为选项类型添加绑定操作符,但似乎每次尝试都会有一些非显而易见的限制阻止我这样做。我怀疑这与.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)
不行。无法根据之前给出的类型信息选择唯一的重载。添加类型注释。
我尝试过将运算符设置为内联,但仍然出现相同的错误。
然后我想到可以将 >>=
运算符作为类型的成员。我很确定这会起作用,但我不认为我可以在现有类型上进行修改。您可以使用 type Option<'a> with
扩展现有类型,但无法将运算符作为扩展。
这是我对这段代码的最后尝试:
type Option<'a> with
static member (>>=) (m : Option<'a>, f : ('a -> Option<'b>)) =
match m with
| None -> None
| Some x -> f x
扩展成员无法提供运算符重载。考虑将运算符定义为类型定义的一部分。很棒。
我还有其他选择吗?我可以在不同的模块中为不同的单子定义单独的函数,但如果您想在同一文件中使用多个版本,那听起来像是地狱。
MyMonad.Operators
,或者不得不使用MyMonad.Operators.>>=
:( - Random DevOption.fs
。 - John Palmeropt >>= (fun x -> ...
似乎并没有比opt |> Option.bind (fun x -> ...
好多少。 - Random Devopt >>= fun x -> ...
,另一个动机是您可以使用相同的顶级缩进。所有你的fun
都在同一个层次上对齐。 - kaefer