Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

let! binding in CE with extension overloads causes FS0072: Lookup on object of indeterminate type #4472

Closed
cmeeren opened this issue Mar 8, 2018 · 6 comments

Comments

@cmeeren
Copy link
Contributor

cmeeren commented Mar 8, 2018

I experience an issue similar to #1310 where I get FS0072 after a let! binding in a computation expression with overloads.

Repro steps

The following AsyncResultBuilder is a stripped-down version of the one in cmeeren/Cvdm.ErrorHandling.

type AsyncResultBuilder() =

  member __.Return (value: 'a) : Async<Result<'a, 'b>> =
    async { return Ok value }

  member __.Bind
      (asyncResult: Async<Result<'a, 'c>>,
       binder: 'a -> Async<Result<'b, 'c>>)
      : Async<Result<'b, 'c>> =
    async {
      let! result = asyncResult
      let bound =
        match result with
        | Ok x -> binder x
        | Error x -> async { return Error x }
      return! bound
    }

[<AutoOpen>]
module Extensions =

  // Having Async<_> members as extensions gives them lower priority in
  // overload resolution between Async<_> and Async<Result<_,_>>.
  type AsyncResultBuilder with

    member this.Bind
        (asnc: Async<'a>,
         binder: 'a -> Async<Result<'b, 'c>>)
        : Async<Result<'b, 'c>> =
      let asyncResult = async {
        let! x = asnc
        return Ok x
      }
      this.Bind(asyncResult, binder)


let asyncResult = AsyncResultBuilder()


let f () =
  asyncResult {
    let! str = asyncResult { return "" }
    return str.Length
           ^^^^^^^^^^
  }

Expected behavior

Everything compiles and works fine.

Actual behavior

I get the following error on str.Length:

error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.

Note that intellisense is able to determine the type:

image

Known workarounds

Add a type annotation:

let! (str: string) = asyncResult { return "" }

The problem also disappears if you remove the extension overload, but this of course has other undesired consequences.

Related information

F# 4.3.4, .NET Standard 2.0 project, VS 15.6.0, latest VF# tools nightly 15.6.0.18030804.

@dsyme
Copy link
Contributor

dsyme commented Mar 8, 2018

@cmreen I believe this is by design - adding method overloads (including extension members) can interfere with the applicability of some type inference rules.

@cmeeren
Copy link
Contributor Author

cmeeren commented Mar 9, 2018

Thanks for the clarification.

  1. You say "by design", but your use of the word "interfere" seems to imply that this is more of a technical limitation and/or conscious choice to not spend time fixing/implementing. Would it be possible to add support for type inference in this case? And since Intellisense manages to correctly infer the type, isn't this a strong indication that it is indeed possible? (And looking at the code I sent, it's clear which overload is being called and thus which type the return value should have, so it seems to my inexperienced eyes that this shouldn't be too difficult, at least not conceptually.)

  2. Are there any way to have overloads like this in CEs (e.g. for Async<_> and Async<Result<_,_>>) and still get fully functional type inference?

@cmeeren
Copy link
Contributor Author

cmeeren commented Mar 9, 2018

  1. I think an asyncResult CE makes sense for simplified asynchronous error handling, but for all I know it's possible that creating a CE that combines and works with several wrapper types like Async<_> and Result<_,_> is using the wrong tool for the job. Besides point 2 above, are there any other ways to make handling errors asynchronously simple, when one has usecases like the one described under the asyncResult section in cmeeren/Cvdm.ErrorHandling/README.md?

@dsyme
Copy link
Contributor

dsyme commented Mar 9, 2018

@cmeeren We have historically made improvements to type inference to allow better inference in the presence of overloads, especially in the context of Linq. So improvements are possible though have to be done very carefully.

Overloading between X<T> and X<Container<T>> is a particularly difficult case, as T can be instantiated to Container<...>.

The fact that mouse-hover tips knows the answer is because mouse-hover tips use the final, global results of type inference, not the partial results which are used in method overload resolution. There's a separate issue on that though it's been like that a long time.

Could you add this as a suggestion at http://github.com/fsharp/fslang-suggestions? We can't do things on a case-by-case basis, but over time can collect a lot of related cases and look at making a technical tweak.

@cmeeren
Copy link
Contributor Author

cmeeren commented Mar 9, 2018

Mentioned in the type inference umbrella issue (fsharp/fslang-suggestions#594). I'm not sure what the actual root cause is and thus don't really know what I should write in a separate RFC.

@dsyme
Copy link
Contributor

dsyme commented Mar 10, 2018

@cmeeren OK, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants