克莱斯利组合运算符>=>,在Haskell圈子中也被称为“鱼”,在许多需要组合特定函数的情况下可能会派上用场。它的工作原理有点像>>运算符,但它不是组成简单的函数'a -> 'b,而是赋予它们一些特殊的属性,最好用'a -> m<'b>来表示,其中m要么是一个类似monad的类型,要么是函数返回值的某个属性。
在更广泛的F#社区中可以找到这种做法的证据,例如,在Scott Wlaschin的Railway oriented programming (part 2)中,作为返回Result<'TSuccess,'TFailure>类型的函数的组合。
推理出哪里有绑定,哪里就一定有鱼,我试着用绑定函数本身来参数化规范的克莱斯利操作符的定义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应该是一个函数),而且我没有看到一种自然的方式来注释它。
在没有形式函数器和单体的情况下,我如何在这里进行抽象,而不必为每种类型显式定义克莱斯利运算符?
发布于 2017-03-16 03:08:51
如果没有更高的种类,你就不能以自然的方式做到这一点。
fish的签名应该是这样的:
let (>=>) (f:'T -> #Monad<'U>``) (g:' U -> #Monad<'V>) (x:'T) : #Monad<'V> = bind (f x) g这在当前的.NET类型系统中是不能表示的,但您可以用您特定的monad替换#Monad,即:Async,并在实现中使用其相应的绑定函数。
话虽如此,如果你真的想使用一个通用的fish操作符,你可以使用F#+,它已经通过使用静态约束进行了定义。如果你看一下第五个code sample here,你会看到它在不同类型上的作用。
当然,您也可以定义自己的代码,但为了让它在大多数常见场景中都能正常运行,需要编写很多代码。您可以从库中获取代码,或者如果您愿意,我可以编写一个小的(但有限的)代码示例。
通用鱼类是在this line中定义的。
我认为一般来说,在使用运算符时,您确实感觉到缺少泛型函数,因为正如您所发现的,您需要打开和关闭模块。这与函数不同,你可以在函数前面加上模块名,你也可以使用运算符(比如Option.(>=>)),但这样就违背了使用运算符的全部目的,我的意思是,它不再是运算符了。
https://stackoverflow.com/questions/42816639
复制相似问题