> 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.
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).
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/LanguageE...