Functionally spoken, LINQ is nothing but a syntactic simplification of expressing monads. Linq to Objects (List-comprehensions - even this would already be extremely useful), which you have been talking about, is just one possible application of this (similar to the List-Monad in Haskell).
If you write
from x in expr1
from y in expr2
select x + y
it's nothing but
do
x <- expr1
y <- expr2
return $ x + y
in Haskell.
The concrete thing that is done depends on user-defined Linq-providers (Extension-Methods) of which Linq.Enumerable
is just one implementation involving IEnumerable
s.
By providing one, you can create completely new LINQ-semantics for your types.
Example: Given an Option
type for computations that may fail (nullable values), one could define a Linq-provider for querying over them.
public static class MaybeExtensions
{
public static Option<T> ToMaybe<T>(this T value)
{
return Option<T>.Some(value);
}
public static Option<U> SelectMany<T, U>(
this Option<T> m,
Func<T, Option<U>> k)
{
return !m.IsNone ? Option<U>.None : k(m.Value);
}
public static Option<V> SelectMany<T, U, V>(
this Option<T> m,
Func<T, Option<U>> k,
Func<T, U, V> s)
{
return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToMaybe()));
}
}
This would now allow us to write such code:
var sum = from x in ReadNumber("x")
from y in ReadNumber("y")
select x + y;
The computation will only return a value if all computations succeeded and will otherwise fail at the first failing one.
In combination with expression trees, Linq can be extremely powerful and allows you to express -
- Database accesses
- Asynchronous programm flow
- Maybe-Monads
- List comprehensions
- Recursive descent parsers
- Continuations
- Mini-languages
- Parallel computations (PLinq)
Some links:
Combined with fixed-point combinators, Linq provides a complete functional mini-language (Linq raytracer).
Note that Scala and F# both have similar concepts in for-comprehensions and computation expressions both being monadic abstractions:
Scala:
for (x <- expr1
y <- expr2) yield x + y
F#:
monad {
let! x = expr1
let! y = expr2
return x + y
}