gpt4 book ai didi

f# - 为什么 F# 不能解决 Async<> 和 Async> 之间的过载?

转载 作者:行者123 更新时间:2023-12-04 22:40:10 26 4
gpt4 key购买 nike

我想在特定上下文中更好地理解 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# 不能选择最具体的重载?
  • 在这种特定情况下,有什么办法可以避免使用包装器类型吗?


  • 编辑: 根据评论中的要求,这里的非编译代码显示了我理想中想要的内容,但不起作用。
    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()
    }

    最佳答案

    F# 重载解析有很多问题,它在规范中有一些规则,但在实践中它不尊重它们。我厌倦了报告有关它的错误,并看到它们在许多情况下是如何以(废话)“按设计”解决方案关闭的。

    您可以使用一些技巧使重载优于另一个。 Builders 的一个常见技巧是将其定义为扩展成员,因此它的优先级较低:

    module AsyncResult =
    let AsyncMap f x = async.Bind(x, async.Return << f)

    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 (AsyncMap Ok asnc)

    type AsyncResultBuilder() =

    member __.Return value = pure value
    member __.ReturnFrom value = returnFrom value
    member __.Bind (result, binder) = bindResult binder result
    member __.Bind (asyncResult, binder) = bind binder asyncResult


    let asyncResult = AsyncResultBuilder()

    open AsyncResult
    type AsyncResultBuilder with
    member __.Bind (async, binder) = bindAsync binder async


    // Usage

    let functionReturningAsync () =
    async { return 2 }

    let functionReturningAsynResult () =
    async { return Ok 'a' }

    let errorHandlingFunction () =
    asyncResult {
    let! x = functionReturningAsync()
    let! y = functionReturningAsynResult()
    let! z = Ok "worked"
    return x, y, z
    }

    话虽如此,我 100% 同意 @fyodor-soikin 的观点,因为他解释的原因,做这种魔法并不是一个好主意。

    但看起来并不是每个人都同意这一点,除了 Chessie,如果你看看 AsyncSeq,例如它有一些这种魔法。

    多年来,我因滥用重载而受到批评,尽管我遵循严格和普遍接受的一般规则以一致的方式进行。所以我认为社区中存在相互矛盾的方法。

    关于f# - 为什么 F# 不能解决 Async<> 和 Async<Result<>> 之间的过载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47697022/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com