I have a special need for a kind of reverse Maybe monad that when being “Nothing” will continue to try to get a value and will keep the first valid value that it gets.
I have tried to outline what I mean here:
public void Test()
{
var r = from x in SomeActionToGetResult()
from y in Stop()
from z in SomeOtherActionToGetResult()
select x | y | z;
}
public Maybe<Result> SomeActionToGetResult()
{
return new Result().Unit();
}
public Maybe<Result> SomeOtherActionToGetResult()
{
return new Result().Unit();
}
public Maybe<Result> Stop()
{
return new Nothing<Result>();
}
The Result class looks like this:
public class Result
{
public static Result operator |(Result a, Result b)
{
if (a != null)
return a;
return b;
}
}
The Unit function is like normal, but the Bind functions gives me trouble:
public static Maybe<U> Bind<T, U>(this Maybe<T> m, Func<T, Maybe<U>> k)
{
if (!m.HasAValidValue)
return k(m.Value);
return m; // <--- problem right here
}
As can be seen, I will – given that m has no value – try to run k() to obtain a valid value. If m already has a value I will like to use that and never run k().
T and U start out as the same type – but as soon as the anonymous types in the linq query kicks in, I can no longer rely on that.
Is this at all possible to do?
(This may not be any help to you, in which case I’ll just delete it. If you’re playing around with monads this is quite possibly teaching my grandmother to suck eggs, but…)
Are you aware that C# already has this at a language level with the null-coalescing operator?
If you need to achieve this in a more library-based form, that’s fine (and I’ll see what I can think of) but if you can just use the existing operator, I would.
EDIT: I’ve had a go at your puzzle, and I think basically it’s not a good fit for LINQ. The problems are mostly to do with the types that LINQ expects in
SelectMany. For example, your second line is effectively transformed to:Here the problems are:
xin theStop()expression, and logically can’t as we only want it to be executed ifxevaluates toNoneTpart of the return type ofSomeActionToGetResult(), so we can’t convert it into aMaybe<T>. We’d have to introduce a new type to extract them both.Then later when we have:
Is that logically the only thing you could have? Would it make sense to have
and if so, what would that mean? We can’t avoid
SomeActionToGetResult()from being executed to start with, as the extension method only works on the result of that method call… so either the projection has to be ignored and you always evaluatex, theny, thenz– or you accept that the ordering won’t always be preserved exactly.Fundamentally I suspect you haven’t really got a monad here, otherwise I’d expect it to fit – but I’m not really competent enough in the maths to say.