I'm reading the book Real-world functional programming by Tomas Petricek and Jon Skeet and I'm having a hard time digesting the section on computation expressions1) (aka monads).
Through this book, I learnt that — contrary to my previous experiences — LINQ query expressions aren't restricted to IEnumerable<T>
, but can work on other custom types as well. This seems very interesting to me, and I am wondering if there are scenarios where the query expression syntax (from x in ... select ...
) would be a nice fit.
Apparently, such custom types are called computation types, which are portrayed as being essentially the same thing as monads in Haskell. I have never been able to grasp what exactly monads are, but according to the book, they are defined through two operations called bind and return.
In functional programming, the type signatures of these two operations would be (I think):
// Bind : M<A'> -> (A' -> B') -> M<B'>
//
// Return : A' -> M<A'>
where M
is the monadic type's name.
In C#, this corresponds to:
Func< M<A>, Func<A,B>, M<B> > Bind;
Func< A, M<A> > Return;
It turns out that LINQ's Enumerable.Select
(the projection operator) has exactly the same signature as the bind operation with M := IEnumerable
.
Using this knowledge, I can now write a custom computation type which is not IEnumerable
:
// my custom computation type:
class Wrapped<A>
{
// this corresponds to the Return operation:
public Wrapped(A value)
{
this.Value = value;
}
public readonly A Value;
}
static class Wrapped
{
// this corresponds to the Bind operation:
public static Wrapped<B> Select<A, B>(this Wrapped<A> x, Func<A,B> selector)
{
return new Wrapped<B>(selector(x.Value));
}
}
And now I can use Wrapped<T>
in LINQ query expressions, e.g.:
Wrapped<int> wrapped = new Wrapped<int>(41);
Wrapped<int> answer = from x in wrapped // works on int values instead
select x + 1; // of Wrapped<int> values!
Of course this example is not very useful, but it demonstrates how query expressions can be made to do something else than working with collections, e.g. wrapping and unwrapping values with some type.
The above computation type doesn't seem very useful. Therefore I wonder, what other reasonable uses (besides processing collections) could there be that make use of LINQ query expressions?
1) Section 12.4: "Introducing alternative workflows", starting on page 334.
Language-Integrated Query (LINQ) is the name for a set of technologies based on the integration of query capabilities directly into the C# language. Traditionally, queries against data are expressed as simple strings without type checking at compile time or IntelliSense support.
The Any operator is used to check whether any element in the sequence or collection satisfy the given condition. If one or more element satisfies the given condition, then it will return true. If any element does not satisfy the given condition, then it will return false.
LINQ is monad. It is very carefully designed by Erik Meijer so that it is monad.
A query expression defines the search criteria for retrieving documents using ConText. A query expression consists of query terms (words and phrases) and other components such as operators and special characters which allow users to specify exactly which documents are retrieved by ConText.
Thoughts:
IEnumerable<T>
pull model)from
(SelectMany
) etc to pick out branch/merge points completely unrelated to enumerablesIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With