如何在F#中使用fish(>>=,Kleisli复合)运算符实现map?
用>>=运算符简洁实现Result的map函数
我正在学习Scott Wlaschin的《面向铁路编程》里的单子复合。定义了bind、switch和>>=(fish运算符)之后,文章引入了map来展示如何把单轨函数转成双轨函数:
f: a -> b => f': T<a,c> -> T<b,c>
文章里的map实现是:
let map oneTrackFunction twoTrackInput = match twoTrackInput with | Success s -> Success (oneTrackFunction s) | Failure f -> Failure f
我已经用switch和bind实现了等价的简洁版本:
let map' f = bind (switch f)
但尝试用>>=实现时,只写出了不够简洁的代码:
let map'' f result = match result with | Ok o -> ((fun _ -> result) >>= (switch f)) o | Error e -> Error e
(注:如果result类型是Result<'a,'c>,o可以是任意'a类型值,因为f的输入已经存在作为>>=第一个操作数的闭包里,这是我能想到保持泛型的唯一方式)
请问有没有类似map'的更简洁实现?
测试示例:
map ((+) 2) ((Ok 27) : Result<int,string>)
用到的bind、switch和>>=实现:
let bind ( f : 'a -> Result<'b,'c>) (result : Result<'a,'c>) = match result with | Ok o -> f o | Error e -> Error e let switch (f : 'a -> 'b) (x : 'a ) = f x |> Ok let (>>=) (f : 'a -> Result<'b,'error>) (g : 'b -> Result<'c,'error>) = f >> (bind g)
简洁实现方案
当然有,你可以利用id函数(恒等函数,即fun x -> x)和>>=组合出和map'风格一致的简洁实现:
let map'' f = id >>= switch f
原理说明
>>=的定义是接受两个返回Result的单轨函数,返回一个新的单轨函数:先执行左函数得到Result,再用bind把右函数应用到这个结果上。id作为左操作数,传入任意result后直接返回result本身,等价于fun result -> result。switch f是把单轨函数f转换成返回Result的单轨函数,即fun x -> Ok (f x)。- 所以
id >>= switch f展开后就是fun result -> bind (switch f) result,和你之前写的map'完全等价,功能和原始map一致。
如果觉得用id不够直观,也可以写成显式的匿名函数形式,但id的写法最简洁:
let map'' f = (fun x -> x) >>= switch f
内容的提问来源于stack exchange,提问作者toraritte
相关产品推荐
相关产品推荐

