Подтвердить что ты не робот

Выражения запросов 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.

4b9b3361

Ответ 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 # для чего-то ближе к дому) и почувствуйте, как монады используются в реальном коде.

Теперь проблема в том, что это может быть не очень идиоматический код на С#, даже если он работает и поддерживается, и т.д. Важно не бороться с языком, а не писать код, который вы только можете понять. По крайней мере, ваша команда должна быть "на нем".