3 ms·
Yes, lack of the higher kind polymorphism makes it harder to express these abstract types universally, but only concrete implementations with Select and SelectM
by namelosw 7y ago
Yes, lack of the higher kind polymorphism makes it harder to express these abstract types universally, but only concrete implementations with Select and SelectMany.
SelectMany is similar to a monadic bind, with an extra selector parameter in its signature, you can achieve the following expression:
from one in Maybe.Of(1)
from two in MaybeAddOne(one)
Select two
The first line will become a Select call and the second line will become a SelectMany call.
Another thing is async/await is pretty much similar to LinQ expressions, but it's hardcoded with the type Task.
- louthy 7y ago> Yes, lack of the higher kind polymorphism makes it harder to express these abstract types universally Here's a way of doing ad-hoc polymorphism to implement a more general monad in C#. It's limited in that the return type for Bind can be any monad (although it still type checks). There are a few other issues (not least it's ugly as hell). But, it does allow for building of very general monadic operations (along with other ad-hoc polymorphic types). // Typeclass (ish) public interface Monad<MA, A> { MA Return(A x); MA Fail(object error = null); MB Bind<MonadB, MB, B>(MA ma, Func<A, MB> f) where MonadB : struct, Monad<MB, B>; } // Class instance of Monad for Option public struct MOption<A> : Monad<Option<A>, A> { public MB Bind<MonadB, MB, B>(Option<A> ma, Func<A, MB> f) where MonadB : struct, Monad<MB, B> => ma is Some<A> x ? f(x.Value) : default(MonadB).Fail(); public Option<A> Return(A x) => new Some<A>(x); public Option<A> Fail(object error = null) => new None<A>(); } // Option 'discriminated union' public interface Option<A> { } public class Some<A> : Option<A> { public readonly A Value; public Some(A value) => Value = value; } public class None<A> : Option<A> { public None() { } } // Testing adding any two M<int> types together. Doesn't need to just work with // ints, it's just a simple example. public class Test { public void Test1() { // Some 200 var r1 = AddAnyMonads<MOption<int>, Option<int>>(new Some<int>(100), new Some<int>(100)); // None var r2 = AddAnyMonads<MOption<int>, Option<int>>(new Some<int>(100), new None<int>()); } public MInt AddAnyMonads<MonadInt, MInt>(MInt ma, MInt mb) where MonadInt : struct, Monad<MInt, int> => default(MonadInt).Bind<MonadInt, MInt, int>(ma, x => default(MonadInt).Bind<MonadInt, MInt, int>(mb, y => default(MonadInt).Return(x + y))); } I have a more complete example in language-ext [1] which unifies monads that take inputs (like Reader and State) and produce output (like Writer) with the more simple monads like Option. [1] https://github.com/louthy/language-ext/blob/master/LanguageExt.Core/TypeClasses/Monad/Monad.cs https://github.com/louthy/language-ext/blob/master/LanguageE...