[英]Proper way of applying two (or many) option values to a function in F#
最近我发现了一种在功能世界中非常有用(和漂亮)的编程风格,称为面向铁路的编程 。 例如,当想要创建一个产生选项类型的函数管道时,我们想要返回None,如果它们中的任何一个失败,我们可以这样做:
someOptionValue // : 'a option
>>= func1 // func1: 'a -> 'b option
>>= func2 // func2: 'b -> 'c option
and so on...
其中(>>=) : 'a option -> (a' -> 'b option) -> 'b option
操作的值适用于左手侧,如果是Some value
或None
其他。
但是,这是我的问题,如果我们有一个“接受”两个(或许多)选项类型的函数,假设funcAB : 'a -> 'b -> 'c option
, valA : 'a option
和valB : 'b option
,我们仍然想创建这个管道或使用一些不错的运算符(不是专门为此创建一个新的,但使用一些标准的方法,特别是我不想使用match ... with
来“解包”选项值)
目前我有这样的事情:
valA
>>= (funcAB >> Some)
>>= (fun ctr -> valB >>= ctr)
但是看起来并不“正确”(或者有趣的是更好的词;]),如果函数需要更多参数或者我们想要创建更长的管道,它就无法很好地扩展。 有一个更好的方法吗?
我使用过F#语法,但我认为这个问题可以应用于任何函数式编程语言,如OCaml和Haskell。
编辑(解决方案):
感谢chi的回答,我创建了以下代码F#,这比我以前的代码更加惯用:
funcAB <!> valA <*> valB |> Option.flatten
如果我们有更多值,它看起来很好: funcAB <!> valA <*> valB <*> valC <*> ...
我使用了YoLo中定义的运算符。
在Haskell中,我们可以使用Applicative
语法:
如果
valA :: f a
valB :: f b
funAB :: a -> b -> f c
然后
join $ funAB <$> valA <*> valB :: f c
如果f
是monad(就像Maybe
,Haskell的option
)。
我想,只要你定义你的运算符,它也应该适应F#
(<$>) :: (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
join :: f (f a) -> f a
上面的技巧是一个穷人的Idris版本! - 注释(轰号) 。
另一个常见的选择是使用do
do a <- valA
b <- valB
funAB a b
但这与使用>>=
相当,确实:
valA >>= \a ->
valB >>= \b ->
funAB a b
并不复杂得多。
一种选择是使用计算表达式 。 对于option
,没有标准的,但您可以轻松创建自己的:
type OptionBuilder() =
member this.Bind (x, f) = Option.bind f x
member this.Return x = Some x
let optional = OptionBuilder()
let a, b = Some(42), Some(7)
let f x y = x + y
let res = optional {
let! x = a
let! y = b
return f x y
}
这非常类似于Haskells do
记号。 有关更高级的功能,请查看F#+ ,它还具有通用的applicative functor运算符<*>
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.