Я тоже думаю, что это очень полезная аналогия. Фактически, я использовал именно эту аналогию при описании оператора конвейеризации в моей книге Функциональное программирование в реальном мире (которая пытается объяснить функциональные идеи людям с фоном C #). Ниже приводится цитата из главы 6.
Что касается различий между ними - существуют некоторые концептуальные различия (например, методы расширения «добавление элементов к объектам»), но я не думаю, что это влияет на то, как мы их используем на практике.
Оператор конвейеризации (|>
) позволяет нам написать первый аргумент для функции в левой части; перед именем функции. Это полезно, если мы хотим вызвать несколько функций обработки для некоторого значения в последовательности, и мы хотим записать значение, которое обрабатывается первым. Давайте рассмотрим пример, показывающий, как инвертировать список в F #, а затем взять его первый элемент:
List.hd(List.rev [1 .. 5])
Это не очень элегантно, поскольку операции записываются в обратном порядке, в котором они выполняются, а обрабатываемое значение находится с правой стороны, в нескольких скобках. Используя методы расширения в C #, мы написали бы:
list.Reverse().Head();
В F # мы можем получить тот же результат, используя оператор конвейеризации:
[1 .. 5] |> List.rev |> List.hd
Хотя это может показаться сложным, на самом деле оператор очень прост. У него есть два аргумента - второй (справа) является функцией, а первый (слева) является значением. Оператор дает значение в качестве аргумента функции и возвращает результат.
В некоторых смыслах конвейеризация похожа на вызов методов с использованием точечной нотации для объекта, но она не ограничивается внутренними методами объекта. Это похоже на методы расширения, поэтому, когда мы пишем альтернативу C # функции F #, которая обычно используется с оператором конвейерной обработки, мы реализуем ее как метод расширения.