我想为这两个表达式构建一个计算表达式。
这很简单
type Result<'TSuccess> =
| Success of 'TSuccess
| Failure of List<string>
type Foo = {
a: int
b: string
c: bool
}
type EitherBuilder () =
member this.Bind(x, f) =
match x with
| Success s -> f s
| Failure f -> Failure f
member this.Return x = Success x
let either = EitherBuilder ()
let Ok = either {
let! a = Success 1
let! b = Success "foo"
let! c = Success true
return
{
a = a
b = b
c = c
}
}
let fail1 = either {
let! a = Success 1
let! b = Failure ["Oh nose!"]
let! c = Success true
return
{
a = a
b = b
c = c
}
} //returns fail1 = Failure ["Oh nose!"]
但在失败(多次)的情况下,我想累积这些并返回失败,如下所示。
let fail2 = either {
let! a = Success 1
let! b = Failure ["Oh nose!"]
let! c = Failure ["God damn it, uncle Bob!"]
return
{
a = a
b = b
c = c
}
} //should return fail2 = Failure ["Oh nose!"; "God damn it, uncle Bob!"]
我有一个关于如何通过重写来做到这一点的想法Bind
并且总是回来Success
(尽管有一些额外的结构表示累积的错误)。但是,如果我这样做,那么我会错过停止信号,并且我总是会返回返回值(实际上并不是因为我会遇到运行时异常,但原则上)
我认为你想要做的事情不能用单子来表达。问题是Bind
如果可以获取函数参数的值,则只能调用其余的计算(这可能会产生更多失败)。在你的例子中:
let! a = Success 1
let! b = Failure ["Oh nose!"]
let! c = Failure ["God damn it, uncle Bob!"]
绑定无法调用以以下开头的延续b
因为Failure ["Oh nose!"]
不提供价值b
。您可以使用默认值并将错误保留在一边,但这会改变您正在使用的结构:
type Result<'T> = { Value : 'T; Errors : list<string> }
您可以使用应用函子抽象来编写此代码,您需要:
Merge : F<'T1> * F<'T2> -> F<'T1 * 'T2>
Map : ('T1 -> 'T2) -> M<'T1> -> M<'T2>
Return : 'T -> M<'T>
您可以通过以下方式实现所有这些Merge
累积错误(如果两个参数都表示失败)并且Map
仅当没有值时才应用计算。
在 F# 中对应用函子进行编码有多种方法,但没有合适的语法,因此您很可能最终会使用丑陋的自定义运算符。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)