Я читаю книгу Функциональное программирование в реальном мире Томаса Петричека и Джона Скита, и мне сложно переварить раздел на вычисленных выражениях 1) (также как монады).
В этой книге я узнал, что — вопреки моему предыдущему опыту; Выражения запроса LINQ не ограничены IEnumerable<T>
, но могут работать и с другими настраиваемыми типами. Мне кажется, это очень интересно, и мне интересно, существуют ли сценарии, где синтаксис выражения запроса ( from x in ... select ...
) было бы неплохо.
Некоторая справочная информация:
По-видимому, такие настраиваемые типы называются типами вычислений, которые изображаются как по существу то же самое, что и монады в Haskell. Я никогда не мог понять, что такое монады, но, согласно книге, они определяются двумя операциями, называемыми bind и return.
В функциональном программировании сигнатуры типов этих двух операций будут (я думаю):
// Bind : M<A'> -> (A' -> B') -> M<B'>
//
// Return : A' -> M<A'>
где M
- имя монадического типа.
В С# это соответствует:
Func< M<A>, Func<A,B>, M<B> > Bind;
Func< A, M<A> > Return;
Оказывается, что LINQ Enumerable.Select
(оператор проектирования) имеет точно такую же подпись, что и операция связывания с M := IEnumerable
.
Мой пользовательский тип вычислений LINQ:
Используя это знание, теперь я могу написать собственный тип вычислений, который не является 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));
}
}
И теперь я могу использовать Wrapped<T>
в выражениях запроса LINQ, например:
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!
Конечно, этот пример не очень полезен, но он демонстрирует, как выражать выражения запроса, чтобы сделать что-то еще, чем работать с коллекциями, например. обертывание и разворачивание значений с помощью какого-либо типа.
Вопрос:
Вышеуказанный тип вычисления не очень полезен. Поэтому мне интересно, какие другие разумные способы использования (помимо обработки коллекций) могли бы возникнуть, используя выражения запроса LINQ?
1) Раздел 12.4: "Представляем альтернативные рабочие процессы", начиная со страницы 334.