Kleisli组合运算符>=>
,也称为Haskell圈中的“鱼”,在需要组合专用函数的许多情况下可能会派上用场。它的工作方式类似于>>
运算符,但它不是组合简单函数'a -> 'b
,而是赋予它们一些特殊属性,最好用'a -> m<'b>
表示,其中m
是类似monad的类型或函数返回值的某些属性。
可以找到更广泛的F#社区中这种做法的证据,例如在Scott Wlaschin的Railway oriented programming (part 2)作为返回Result<'TSuccess,'TFailure>
类型的函数组合。
推测在有绑定的地方,必须还有fish,我尝试使用绑定函数本身对规范的Kleisli运算符的定义let (>=>) f g a = f a >>= g
进行参数化:
let mkFish bind f g a = bind g (f a)
这非常有用,但通常不应该在面向用户的代码上释放特殊操作符。我可以编写返回选项的函数......
module Option =
let (>=>) f = mkFish Option.bind f
let odd i = if i % 2 = 0 then None else Some i
let small i = if abs i > 10 then None else Some i
[0; -1; 9; -99] |> List.choose (odd >=> small)
// val it : int list = [-1; 9]
...或者我可以将函数应用程序设计到堆栈的两个最高值并将结果推回去,而不必引用我明确操作的数据结构:
module Stack =
let (>=>) f = mkFish (<||) f
type 'a Stack = Stack of 'a list
let pop = function
| Stack[] -> failwith "Empty Stack"
| Stack(x::xs) -> x, Stack xs
let push x (Stack xs) = Stack(x::xs)
let apply2 f =
pop >=> fun x ->
pop >=> fun y ->
push (f x y)
但困扰我的是签名val mkFish : bind:('a -> 'b -> 'c) -> f:('d -> 'b) -> g:'a -> a:'d -> 'c
毫无意义。类型变量处于混乱的顺序,它过于笼统('a
应该是一个函数),而且我没有看到一种自然的注释方式。
如果没有正式的仿函数和monad,我怎么能在这里抽象,而不必为每种类型明确定义Kleisli运算符?
答案 0 :(得分:4)
如果没有高级种类,你不能以自然的方式做到这一点。
鱼的签名应该是这样的:
let (>=>) (f:'T -> #Monad<'U>``) (g:' U -> #Monad<'V>) (x:'T) : #Monad<'V> = bind (f x) g
在当前的.NET类型系统中无法代表。
话虽如此,如果你真的想使用泛型鱼类运算符,你可以使用已经使用静态约束定义它的F#+。如果你看第5 code sample here,你会看到它在不同类型的行动中。
当然你也可以定义自己的,但是要编写很多东西,以便在大多数常见场景中使其表现正常。您可以从库中获取代码,或者如果您愿意,我可以编写一个小的(但有限的)代码示例。
通用鱼在this line中定义。
我认为通常你在使用运算符时确实感觉缺乏泛型函数,因为正如你所发现的,你需要打开和关闭模块。它不像你用模块名称作为前缀的函数,你也可以用运算符(类似Option.(>=>)
)这样做,但是它失败了使用运算符的全部目的,我的意思是&# 39; s不再是运营商。