Выражения запросов LINQ, которые работают с типами (monads?), Отличными от IEnumerable <T> - Возможные применения?
Я читаю книгу Функциональное программирование в реальном мире Томаса Петричека и Джона Скита, и мне сложно переварить раздел на вычисленных выражениях 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.
Ответы
Ответ 1
Несмотря на то, что мне не нравится это делать (поскольку это немного напоминает обман), я думаю, что на этот раз я должен ответить на свой вопрос.
Я подумал еще об этом. Мой вопрос был несколько наивным. Дело в том, что выражения запроса LINQ (например, from
& hellip; in
& hellip; where
& hellip; select
& hellip;), а также foreach
являются синтаксическим сахаром поверх другого, более основного синтаксиса.
-
foreach
работает во всем, что реализует метод IEnumerator<T> GetEnumerator()
. IEnumerable<T>
просто выполняет это условие.
-
Аналогично, выражения запроса LINQ переводятся в соответствии с некоторыми четко определенными правилами, например. from x in xs where x > 0 select -x
становится xs.Where(x => x > 0).Select(x => -x)
. Пока какой-то тип реализует некоторые или все методы оператора запроса, этот тип может использоваться с LINQ практически для любых целей.
Что остается от моего вопроса, так это то, на что можно было бы использовать LINQ, помимо обработки коллекций; и я думаю, что ответ будет "для совсем немного", потому что структура выражений запроса LINQ довольно жесткая. Вам всегда нужна часть from
& hellip; in
. Кажется, что select
& hellip; всегда необходимо. Если "язык" полученных выражений не соответствует конкретному потенциальному сценарию, любые другие ключевые слова (let
, orderby
, groupby
и т.д.) Не улучшат ситуацию. LINQ был четко разработан с одной целью, поскольку это запрос данных, и результирующая грамматика фактически ограничивает LINQ больше, чем это возможно.
Я сравнивал возможности LINQ для целей, отличных от запросов данных о возможностях вычислений F #, и они кажутся более гибкими, поскольку не так много требуемых ключевых слов. Это, как правило, делает их пригодными для большего количества сценариев.
Ответ 2
LinqToTwitter использует LINQ необычным способом. Материал в предложении 'from' не является логически перечислимым типом.
Посмотрите на источник:)
Ответ 3
Мысли:
- PushLINQ (me and Jon) - изменяет LINQ на модель push (а не модель
IEnumerable<T>
pull)
- Reactive Framework/Reactive Extensions - еще одна очень разная модель событий, включающая синтаксис LINQ
- Я написал API-интерфейс потоков (ab), используя синтаксис запроса LINQ; Я не был на 100% убежден в этом, поэтому бросил его, но это было интересно; использовал
from
(SelectMany
) и т.д., чтобы выбрать точки ветвления/слияния, полностью не связанные с перечислениями.
Ответ 4
Это полезно по тем же причинам, что и монады полезны в Haskell. Это очень возможно реализовать тип Either<TLeft, TRight>
и снабдить его реализацией реализации запроса, например. Это можно использовать для написания более функционального кода, в котором вы создаете действия в прецеденте с обработкой ошибок и протоколированием, встроенными и т.д. В принципе, взгляните на такие языки, как Haskell (или F # для чего-то ближе к дому) и почувствуйте, как монады используются в реальном коде.
Теперь проблема в том, что это может быть не очень идиоматический код на С#, даже если он работает и поддерживается, и т.д. Важно не бороться с языком, а не писать код, который вы только можете понять. По крайней мере, ваша команда должна быть "на нем".