Hacker News new | past | comments | ask | show | jobs | submit login

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.

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

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact