Kleisli組合運算符>=>
也被稱爲haskell圈子中的「魚」,在需要組合專門功能的許多情況下可能會派上用場。它的工作方式類似於>>
運算符,但不是構成簡單函數'a -> 'b
它賦予它們一些特殊屬性,可能最好用'a -> m<'b>
表示,其中m
是monad-like類型或函數返回值的某個屬性。 在更廣泛的F#社區中可以找到這種做法的證據,例如在Scott Wlaschin的Railway oriented programming (part 2)中作爲返回Result<'TSuccess,'TFailure>
類型的函數的組合。在F#中製作魚
理由是那裏的困境,就必須有同樣的魚,我嘗試用bind函數本身參數化規範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]
...或I可以設計一個功能應用到堆棧的兩個最上面的值和推回結果,而不必引用我操作所述數據結構明確地:
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
應該是一個函數),而我沒有看到自然的方式來註釋它。 如何在沒有正式函數和monads的情況下抽象出這些,而不必爲每種類型明確定義Kleisli運算符?
像往常一樣,靜態解析類型參數來拯救!我想這是唯一可能的答案,至少在全球泛型運算符的明顯用例中是這樣。我正在考慮一些更特別的方法,這裏是我的'bind',給我Kleisli--但是然後在定製的運算符定義中闡明函數應用程序並沒有太多的努力。 – kaefer