F#为什么不能解决Async<>之间的重载?和Async< Result>&gt ;? [英] Why isn't F# able to resolve overload between Async<> and Async<Result<>>?

查看:82
本文介绍了F#为什么不能解决Async<>之间的重载?和Async< Result>&gt ;?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在特定情况下更好地了解F#重载分辨率.

I want to understand the F# overload resolution a little better in a specific context.

我正在编写一个简单的asyncResult工作流程/计算表达式,以使与异步工作流程结合使用时,可以更轻松地使用面向铁路编程风格的错误处理.我通过重载工作流构建器上的Bind方法来做到这一点.这是相当标准的,并且已在我见过的所有指南中使用(并且还用于 Chessie/ErrorHandling.fs ).

I'm writing a simple asyncResult workflow/computation expression to make error handling in the style of railway-oriented programming easier to use when combined with async workflows. I do this by overloading the Bind method on the workflow builder. This is fairly standard and used in all guides I've seen (and is also used in e.g. Chessie/ErrorHandling.fs).

我有一个接受Async<_>的重载和一个接受Result<_,_>的重载.现在,理想情况下,我想要第三个接受Async<Result<_,_>>的重载.但是,当我尝试对返回Async<'a>的表达式使用let!do!时,F#抱怨无法确定唯一的重载,因为Async<_>Async<Result<_,_>>都适合,它们当然可以(尽管一个比另一个更适合).我似乎能够做到这一点的唯一方法就是像Chessie(上面的链接)那样做,并定义一个包装器类型:

I have one overload that accepts an Async<_> and one that accepts a Result<_,_>. Now, ideally, I'd like a third overload that accepts Async<Result<_,_>>. However, when I try to use let! or do! with an expression that returns Async<'a>, F# complains that a unique overload could not be determined, because both Async<_> and Async<Result<_,_>> fits, which of course they do (though one fits more specifically than the other). The only way I seem to be able to do this, is to do like Chessie (link above) and define a wrapper type:

type AsyncResult<'a, 'b> = AR of Async<Result<'a, 'b>>

这又需要我以这种新类型包装对返回Async<Result<_,_>>的方法的所有调用:

This again requires me to wrap all calls to methods that return Async<Result<_,_>> in this new type:

asyncResult {
  let! foo = funcReturningResultInsideAsync() |> AR
  ...
}

AFAIK,C#将选择最具体的重载.如果F#做了同样的事情,那将不是问题.

AFAIK, C# will select the most specific overload. If F# did the same, this would not be a problem.

  1. 为什么F#无法选择最具体的重载?
  2. 在这种特定情况下,可以做些什么来避免包装类型?


:按照注释的要求,这里的非编译代码显示了我理想的状态,但不起作用.


As requested in the comments, here's non-compiling code that shows what I ideally would like, but doesn't work.

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#重载解析是非常多的错误,它在规范中有一些规则,但在实践中不遵守这些规则.我厌倦了报告有关此问题的错误,并看到在许多情况下以(无用的)按设计"解决方案关闭它们的情况.

F# overload resolution is very buggy, it has some rules in the spec but in practice it doesn't respect them. I'm tired of reporting bugs about it and seeing how they're being closed in many cases with a (nonsense) 'by-design' resolution.

您可以使用一些技巧来使重载优于其他技巧.对于Builders来说,一个常见的技巧是将其定义为扩展成员,因此它的优先级会降低:

You can use some tricks to make an overload preferable over the other. One common trick for Builders is to define it as an extension member, so it will have less priority:

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的观点,因为他解释的原因,做这种魔术不是一个好主意.

Having said that, I agree 100% with @fyodor-soikin in that it's not a good idea to do this kind of magic for the reasons he explained.

但是,除了Chessie,如果您查看

But looks like not everybody agrees with this, apart from Chessie if you have a look at AsyncSeq for example it does some of this magic.

多年来,我一直在滥用重载,尽管我遵循严格且普遍接受的规则以一致的方式这样做.因此,我认为社区中存在矛盾的方法.

I was criticized over years for abusing of overloading though I was doing it in a consistent way following strict and common accepted rules in general. So I think there are contradictory approaches in the community.

这篇关于F#为什么不能解决Async&lt;&gt;之间的重载?和Async&lt; Result&gt;&gt ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆