Funzionalmente parlato, LINQ non è altro che una semplificazione sintattica dell'espressione delle monadi. Linq to Objects (List-comprehensions - anche questo sarebbe già estremamente utile), di cui hai parlato, è solo una possibile applicazione (simile alla List-Monad in Haskell).
Se si scrive
from x in expr1
from y in expr2
select x + y
non è niente, ma
do
x <- expr1
y <- expr2
return $ x + y
in Haskell.
La cosa concreta che viene fatto dipende LINQ provider definiti dall'utente (Interne-metodi) di cui Linq.Enumerable
è solo una realizzazione che coinvolge IEnumerable
s.
Fornendone uno, puoi creare semantica LINQ completamente nuova per i tuoi tipi.
Esempio: Dato un tipo Option
per calcoli che potrebbero non riuscire (valori nullable), è possibile definire un provider Linq per eseguire una query su di essi.
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()));
}
}
Ciò consentirà ora di scrivere questo codice:
var sum = from x in ReadNumber("x")
from y in ReadNumber("y")
select x + y;
Il calcolo restituisce solo un valore se tutti i calcoli sono riusciti a pena non riuscire al primo in mancanza di uno.
In combinazione con alberi di espressione, LINQ può essere estremamente potente e permette di esprimere -
- accessi al database
- flusso programma Asynchronous
- maybe-Monadi
- di lista
- ricorsivo parser di discesa
- Continuazioni
- Mini-lingue
- calcoli paralleli (PLINQ)
alcuni link:
Combinato con combinatori a virgola fissa, Linq fornisce un mini-linguaggio funzionale completo (Linq raytracer).
noti che Scala e F # entrambi hanno concetti simili a for-comprensioni e le espressioni di calcolo essendo entrambi astrazioni monadici:
Scala:
for (x <- expr1
y <- expr2) yield x + y
F #:
monad {
let! x = expr1
let! y = expr2
return x + y
}
Grazie per le informazioni. Guarderò quei cavi. – Joe
Davvero, davvero un buon riassunto con un esempio piacevole e di facile comprensione. +1 –
Continuando sul collegamento raytracer, ecco lo stesso raytracer implementato interamente in una singola istruzione LINQ: http://tirania.org/blog/archive/2007/Nov-16.html – JulianR