Я читаю книгу Реальное функциональное программирование Томаса Петрисека и Джона Скита , и мне трудно переварить раздел о выражениях вычислений 1 ) (он же монады).
Из этой книги я узнал, что & mdash; вопреки моему предыдущему опыту & mdash; Выражения запросов LINQ не ограничиваются IEnumerable<T>
, но могут работать и с другими пользовательскими типами. Мне это кажется очень интересным, и мне интересно, существуют ли сценарии, где синтаксис выражения запроса (from x in ... select ...
) было бы неплохо.
Некоторая справочная информация:
По-видимому, такие пользовательские типы называются типами вычислений , которые изображаются как по сути то же самое, что и монады в Haskell . Я никогда не мог понять, что такое монады, но согласно книге они определяются с помощью двух операций, называемых bind и return .
В функциональном программировании сигнатуры типов этих двух операций были бы (я думаю):
// Bind : M<A'> -> (A' -> B') -> M<B'>
//
// Return : A' -> M<A'>
где M
- имя монадического типа.
В C # это соответствует:
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, например ::1010*
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.