Reputation: 32182
I have the following code
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
public void TestEnumOfMaybe()
{
List<Maybe<int>> l = new List<Maybe<int>>();
l.Add(1.ToMaybe());
l.Add(Maybe.None<int>());
l.Add(3.ToMaybe());
var y = from x in l
from y in x
select y;
}
My Maybe type accepts all the Select and SelectMany methods to make it Linq compatible and I have tests to prove it so. However I can't seem to figure out a way to do cross type LINQ composition as in the above test case. The error I get is
expression of type 'FunctionalExtensions.Maybe<int>' is not allowed in
a subsequent from clause in a query expression with source type
'System.Collections.Generic.List<FunctionalExtensions.Maybe<int>>'.
Type inference failed in the call to 'SelectMany'.
FunctionalExtensions*
Is there a way to combine the two LINQ types or am I out of luck here? The complete Maybe.cs implementation is at
https://gist.github.com/4016243
Upvotes: 3
Views: 414
Reputation: 32182
For my specific problem it turns out that having Maybe implement IEnumerable is the way to go and it make supporting IOBservable trivial as well. My two test cases which now pass.
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
public void TestEnumOfMaybe()
{
List<Maybe<int>> l = new List<Maybe<int>>();
l.Add(1.ToMaybe());
l.Add(Maybe.None<int>());
l.Add(3.ToMaybe());
var k = from q in l
from y in q
select y;
k.Should().BeEquivalentTo(new[] { 1, 3 });
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
public void TestObservableOfMaybe()
{
List<Maybe<int>> l = new List<Maybe<int>>();
l.Add(1.ToMaybe());
l.Add(Maybe.None<int>());
l.Add(3.ToMaybe());
var o = l.ToObservable();
var k = from q in o
from y in q
select y;
var m = k.ToEnumerable();
m.Should().BeEquivalentTo(new[] { 1, 3 });
}
In general I think the LINQ system forbids chaining different types together even if the SelectMany signatures will line up. I'm yet to be convinced otherwise.
Upvotes: 0
Reputation: 1062855
As Jon notes, the LINQ translation of from
,from
to SelectMany
is:
var query = l.SelectMany(x => x, (x, y) => y);
The first thing to note is that your SelectMany
is defined against an individual Maybe<T>
, so for this to make sense, l
must be a Maybe<T>
(for some T
), not a list. The next thing to note is that the signature has to match. For example:
public static Maybe<TResult> SelectMany<TSource, TMaybe, TResult>(
this Maybe<TSource> m, Func<Maybe<TSource>, Maybe<TMaybe>> f,
Func<Maybe<TSource>, Maybe<TMaybe>, TResult> g)
{
throw new NotImplementedException();
// return m.Bind(x => f(x).Bind(y => g(x, y).ToMaybe()));
}
And:
var obj = l[0];
Now this works (explicit generics):
var q = obj.SelectMany<int, int, Maybe<int>>((x => x), (x, y) => y);
which is identical to:
var r = obj.SelectMany((x => x), (x, y) => y);
which is identical to:
var query = from x in obj
from y in x
select y;
Upvotes: 0
Reputation: 1500595
The translation of this:
// Changed variable from y to query for clarity
var query = from x in l
from y in x
select y;
is simply:
var query = l.SelectMany(x => x, (x, y) => y);
Note that nothing is being called on y
here.
Now l
is a List<Maybe<int>>
, so you need to try to find an appropriate SelectMany
method which would be valid here. Unless you've added any more, it will look in Enumerable.SelectMany
, and every overload there requires the the first delegate to return an IEnumerable<T>
for some T
.
So that's why it's not working. You could make it work by making Maybe<T>
implement IEnumerable<T>
(presumably either yielding a single result or no results). It's hard to say for sure whether that's what you're aiming for, but you've basically got to get that expansion to work.
Alternatively, you could write a new overload for SelectMany
targeting IEnumerable<T>
, for cases where the result is a Maybe<T>
rather than an IEnumerable<T>
. That would be pretty unintuitive though, IMO.
Upvotes: 6
Reputation: 292435
Your SelectMany
method takes a Maybe<TSource>
as a parameter, but you're using it on a List<Maybe<TSource>>
, so it actually uses the SelectMany
method from Enumerable
. It's not clear from your example what you expect the type of y
to be, but as it is the code doesn't really make sense.
Upvotes: 1