I'm not sure exactly what you're asking here but I'll give it a shot. Consider clarifying the question.
The equivalent in C# to an if
with no else
in a monadic workflow is:
from a in b
where c(a)
select a
Logically this is equivalent to (using your Bind, Return and Zero)
Bind(b, a => c(a) ? Return(a) : Zero)
But C# does not lower a where clause into a SelectMany (which is what C# calls Bind). C# lowers a Where clause in a query comprehension to a call to
Where(M<T>, Func<T, bool>)
In short: C# has arbitrary monadic workflows in the form of query comprehensions; any monadic type with the Select, SelectMany, Where, etc, methods can be used in a comprehension. But it doesn't really generalize to additive monads with an explicit zero. Rather, "Where" is expected to have the semantics of the bind operation I noted above: it should have the same effect as binding a single value onto the end if the item matches the predicate, and the zero value if not.
Plainly "Where" for sequences does that. If you have [a, b, c] and want to filter out b, that's the same as concatenating together [[a], [], [c]]. But of course it would be crazily inefficient to actually build and concatenate all those little sequences. The effect has to be the same, but the actual operations can be much more efficient.
C# is really designed to have support for very specific monads: the sequence monad via yield
and query comprehensions, the continuation comonad via await
, and so on. We didn't design it to enable arbitrary monadic workflows as you see in Haskell.
Does that answer your question?