Fun with pure C# and monads

We are all fascinated by pure languages like Haskell, but unfortunately have to code in C#. We could complain to our neighbor all day, but instead, what if we could also do something really pure in C#. Actually we can, it’s called LINQ:

 
            var sum = from a in new[] { 1 }
                from b in new[] { 2 }
                from c in new[] { 3 }
                where a + b > c
                select a + b + c;

Don’t know about you, looks pretty darn pure to me. No side effects given. However, we can’t do much without side effects in the real world. In Haskell, as a pure language, we have to encapsulate all side effects into monads.
What if we wanted to write pure in C#, but also needed side effects. C# supports side effects, but only in imperative style:

 
            Console.WriteLine(sum);

Seems legit. But to us, functional people, not much fun. What if we could write pure, but still have side effects, whenever needed. Luckily, we can define a fancy monad to help us:

 
        public delegate T IO<out T>();

        public static IO<R> SelectMany<S, C, R>(this IO<S> A, Func<S, IO<C>> foo, Func<S, C, R> bar)
        {
            var a = A();
            var B = foo(a);
            var b = B();
            var c = bar(a, b);
            return () => c;
        }

        public static IO<T> Combine<T>(IO<T> a, Action b)
        {
            return new Func<IO<T>, IO<T>>(x => { b(); return x; })(a);
        }

        public static IO<T> Where<T>(this IO<T> t, Func<T, bool> foo)
        {
            return () => (foo(t()) ? t : Combine(t, () => { Console.WriteLine("We screwed up!"); }))();
        }

        public static IO<R> Select<S, R>(this IO<S> s, Func<S, R> foo)
        {
            return () => new Func<IO<S>, R>(x => foo(x()))(s);
        }

Now let’s do some serious pure coding with side effects:

 
            var A = new IO<int>(() => { Console.WriteLine("A"); return 6; });
            var B = new IO<int>(() => { Console.WriteLine("B"); return 7; });
            var C = new IO<int>(() => { Console.WriteLine("C"); return 10; });

            var sum =
                from a in A
                from b in B
                from c in C
                let x = 10
                where a > b
                select a + b + c + x;

Now that looks real fun to me all right.

Henceforth, you can be really sneaky, and hide side effects in places where your coworker least expects them: in pure LINQ expressions. Happy debugging!

Comments

  • Anonymous
    May 21, 2017
    "Henceforth, you can be really sneaky, and hide side effects in places where your coworker least expects them: in pure LINQ expressions. Happy debugging!"Gee .. thanks ... that would make everyone's day productive ... I would either fire anybody that wrote code like that in C#, or quit that company altogether.
    • Anonymous
      December 28, 2017
      Yeah, it's more of a proof-of-concept, not production service code :)