繁体   English   中英

将两个(或多个)选项值应用于F#中的函数的正确方法

[英]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 valueNone其他。

但是,这是我的问题,如果我们有一个“接受”两个(或许多)选项类型的函数,假设funcAB : 'a -> 'b -> 'c optionvalA : 'a optionvalB : '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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM