functional-csharp-code icon indicating copy to clipboard operation
functional-csharp-code copied to clipboard

Ambiguous call to SelectMany

Open dogwith1eye opened this issue 8 years ago • 3 comments

Here is a link to a gist reproducing the problem i'm running into

Ambiguous call to SelectMany

I'm guessing the compiler can't decide whether to use the SelectMany on Task<T> vs the SelectMany on Task<Validation<T>>.

I'm finding the current design doesn't allow us to implement the query pattern on our custom monad stacks. Currently, the query pattern overloads are automatically included with the import of LaYumb.Functional, so how do we pick which overload our stack should use?

Should maybe the extension methods that implement the linq query pattern be moved into their own namespace and require and explicit import statement?

dogwith1eye avatar Aug 14 '17 19:08 dogwith1eye

Indeed, you've hit it on the nail, @dogwith1eye ...

this is something that escaped both me and my technical reviewers. Normally the compiler should pick the most specific overload available, so it's somewhat disappointing that it can't choose Func<T, Task<Validation<R>>> over Func<T, Task<R>>. But that's how it is, apparently.

what still baffles me is that this code does compile. Go figure...

I too thought about using namespaces to remove ambiguities between different overloads of SelectMany. The problem is that namespaces are imported per file (or per namespace), and you may want to use different monad stacks from within the same file / namespace. So I find this option too restrictive.

Another thought I had is the following:

  • use a custom type TaskValidation<T> to help out the compiler; it simply wraps a Task<Validation<T>>
  • if you have more than 2 from-clauses, use a Stack extension method that turns a Task<Validation<T>> into a TaskValidation<T> (only required on the first from-clause)
  • you then write your query as usual, and use implicit conversion if needed to go back to Task<Validation<T>>

I've implemented this in the task-validation branch, which includes tests to prove that it does compile.

Why don't you build that branch and experiment with it, and tell me what you think?

la-yumba avatar Aug 15 '17 12:08 la-yumba

Yeah I think that's fine. Maybe file a bug with the compiler team that the most specific overload should be used?

To test it out I added a TaskValidation class and a TaskValidationTest class to the console project. Coercing the first call in the workflow to the desired stack does force the compiler to use the correct overloads for the duration of the workflow.

Use desired overload of SelectMany

TestSugared does use the overloads from the TaskValidation class and TestSugaredTest does use the overloads from the TaskValidationTest class. What's odd is there doesn't seem to be anyway to force the desugared version to use the correct stack. I keep getting errors on select no matter what i try.

dogwith1eye avatar Aug 15 '17 13:08 dogwith1eye

That last part is understandable. The LINQ query is interpreted using the ternary SelectMany, while your desugared version uses a binary SelectMany, which I've not implemented since it doesn't seem to be required.

la-yumba avatar Aug 15 '17 13:08 la-yumba