Ambiguous call to SelectMany
Here is a link to a gist reproducing the problem i'm running into
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?
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 aTask<Validation<T>> - if you have more than 2
from-clauses, use aStackextension method that turns aTask<Validation<T>>into aTaskValidation<T>(only required on the firstfrom-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?
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.
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.