Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

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

Search: