【发布时间】:2017-12-07 14:16:00
【问题描述】:
我想在特定上下文中更好地理解 F# 重载解析。
我正在编写一个简单的asyncResult 工作流/计算表达式,以便在与异步工作流结合使用时更易于使用面向铁路的编程风格的错误处理。我通过在工作流构建器上重载Bind 方法来做到这一点。这是相当标准的,并且在我见过的所有指南中都使用过(并且也用于例如Chessie/ErrorHandling.fs)。
我有一个接受Async<_> 的重载和一个接受Result<_,_> 的重载。现在,理想情况下,我想要接受Async<Result<_,_>> 的第三个重载。但是,当我尝试将let! 或do! 与返回Async<'a> 的表达式一起使用时,F# 抱怨无法确定唯一的重载,因为Async<_> 和Async<Result<_,_>> 都适合,它们当然适合(尽管一个比另一个更适合)。我似乎能够做到这一点的唯一方法是像 Chessie(上面的链接)一样并定义一个包装器类型:
type AsyncResult<'a, 'b> = AR of Async<Result<'a, 'b>>
这再次要求我将所有对返回 Async<Result<_,_>> 的方法的调用封装在这种新类型中:
asyncResult {
let! foo = funcReturningResultInsideAsync() |> AR
...
}
AFAIK,C# 将选择最具体的重载。如果 F# 也这样做,这将不是问题。
- 为什么 F# 不能选择最具体的重载?
- 在这种特定情况下,是否可以采取任何措施来避免使用包装器类型?
编辑:根据 cmets 的要求,这里是非编译代码,显示了我理想中想要的,但不起作用。
module AsyncResult =
let liftAsync x =
async { return x }
let pure (value: 'a) : Async<Result<'a, 'b>> =
async { return Ok value }
let returnFrom (value: Async<Result<'a, 'b>>) : Async<Result<'a, 'b>> =
value
let bind (binder: 'a -> Async<Result<'b, 'c>>) (asyncResult: Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncResult
match result with
| Ok x -> return! binder x
| Error x -> return! Error x |> liftAsync
}
let bindResult (binder: 'a -> Async<Result<'b, 'c>>) (result: Result<'a, 'c>) : Async<Result<'b, 'c>> =
bind binder (liftAsync result)
let bindAsync (binder: 'a -> Async<Result<'b, 'c>>) (asnc: Async<'a>) : Async<Result<'b, 'c>> =
bind binder (Async.map Ok asnc)
type AsyncResultBuilder() =
member __.Return value = pure value
member __.ReturnFrom value = returnFrom value
member __.Bind (asyncResult, binder) = bind binder asyncResult
member __.Bind (result, binder) = bindResult binder result
member __.Bind (async, binder) = bindAsync binder async
let asyncResult = AsyncResultBuilder()
// Usage
let functionReturningAsync () =
async { return 2 }
let errorHandlingFunction () =
asyncResult {
// Error: A unique overload for method 'Bind' could not be determined ...
do! functionReturningAsync()
}
【问题讨论】:
-
即使没有编译,您能否发布带有用例的代码?
-
完成,并修复了问题中的一个错误 - 它是
Async<_>-返回不起作用的函数,而不是Async<Result<_,_>。