Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monadic comprehension syntax in C#

I've been hanging out in a #haskell IRC room a couple of days ago and someone mentioned that C# has syntax for doing monadic comprehension. What does this mean?

If I understand correctly, monadic comprehension is just a way of sequencing bind operations, which kind of sounds like a do notation? Is that correct?

The problem is that I don't see this in C#. As far as I know IEnumerable<T> is a monad where SelectMany is its bind function, as its signature is A -> IEnumerable<B>. With a little stretch of imagination, we can do

from x in xs
from y in ys

which translates into (I'm not 100% sure here)

xs.SelectMany(x => ys.Select(y => y), (x, y) => ...)

But even if this is true and we think of LINQ as a monad comprehension syntax, it still only applies to IEnumerable<T>. We do have other monads in C# like Task<T>, but how can we use LINQ on those?

It is possible that many of the assumptions in this question are completele wrong, as I'm still trying to grasp some of the monad magic stuff. Please correct me if I'm wrong :)

like image 809
Jakub Arnold Avatar asked Oct 31 '13 14:10

Jakub Arnold


1 Answers

LINQ query syntax is merely syntactic sugar, and doesn't know anything about IEnumerable<>, which is why you can use it for other things.

If you check the C# language specification, it describes how LINQ's query expressions should be transformed in section 7.16.2

The C# language does not specify the execution semantics of query expressions. Rather, query expressions are translated into invocations of methods that adhere to the query expression pattern (§7.16.3). Specifically, query expressions are translated into invocations of methods named Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBy, and Cast.These methods are expected to have particular signatures and result types, as described in §7.16.3. These methods can be instance methods of the object being queried or extension methods that are external to the object, and they implement the actual execution of the query.

Your specific example is described as

A query expression with a second from clause followed by a select clause

from x1 in e1
from x2 in e2
select v

is translated into

( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )

So, using the variable names from your example, any xs that has a method Treturned SelectMany(Func<Tx,Tys>, Func<Tx,Ty,Treturned>) can be used in a statement like

Treturned returned =
    from x in xs
    from y in ys
    select r;

This will compile exactly when

Treturned returned = xs.SelectMany(x => ys, (x, y) => r);

does, which is any time such a method exists on xs. The fact that SelectMany exists for IEnumerable<> doesn't prevent us from equiping other types with methods or extension methods with the same name.

C# can infer the types for the lambdas from the fact that it knows what xs is, and from that can look up the types to the arguments of xs's SelectMany.

like image 102
Cirdec Avatar answered Oct 11 '22 04:10

Cirdec