Выполняется с (.) . (.)
на самом деле довольно просто, это интуиция за тем, что она делает, что довольно сложно понять.
(.)
доставит вас очень далеко, переписывая выражение в вычисления типа «труба» (подумайте о |
в оболочке). Тем не менее, становится неудобно использовать, как только вы пытаетесь создать функцию, которая принимает несколько аргументов с помощью функции, которая принимает только одну. В качестве примера, давайте определение concatMap
:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)
Избавление от xs
просто стандартная операция:
concatMap f = concat . map f
Однако нет «хороший» способ избавления от f
. Это вызвано тем фактом, что map
принимает два аргумента, и мы хотели бы применить concat
к его окончательному результату.
Вы можете, конечно, применить некоторые pointfree трюки и уйти только с (.)
:
concatMap f = (.) concat (map f)
concatMap f = (.) concat . map $ f
concatMap = (.) concat . map
concatMap = (concat .) . map
Но увы, читаемость этого кода в основном исчезли.Вместо этого мы вводим новый комбинатор, который делает именно то, что нам нужно: примените вторую функцию к окончательному результату первого.
-- .: is fairly standard name for this combinator
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)
concatMap = concat .: map
Прекрасно, вот и все для мотивации. Давайте перейдем к беспроблемному бизнесу.
(.:) = \f g x y -> f (g x y)
= \f g x y -> f ((g x) y)
= \f g x y -> f . g x $ y
= \f g x -> f . g x
Теперь вот интересная деталь. Это еще одна из трюков, которые обычно помогают, когда вы застреваете: мы переписываем .
в свою префиксную форму и пытаемся продолжить оттуда.
= \f g x -> (.) f (g x)
= \f g x -> (.) f . g $ x
= \f g -> (.) f . g
= \f g -> (.) ((.) f) g
= \f -> (.) ((.) f)
= \f -> (.) . (.) $ f
= (.) . (.)
Что касается интуиции, есть такой very nice article, что вы должны читать. Я перефразирую часть о (.)
:
Давайте снова думать о том, что наш комбинатор должен делать: он должен применять f
к результате из результата из g
(я использую конечный результат в перед тем, как это сделать, это действительно то, что вы получаете, когда полностью применяете - modulo объединяющие переменные типа с другим типом функции - g
, результат вот только приложение g x
для некоторых x
).
Что это означает для нас, чтобы нанести f
на результат от g
? Хорошо, как только мы применим g
к некоторому значению, мы возьмем результат и применим к нему f
. Звучит знакомо: вот что делает (.)
.
result :: (b -> c) -> ((a -> b) -> (a -> c))
result = (.)
Теперь, получается, что состав (наша из слов) этих комбинаторов это просто функция композиции, то есть:
(.:) = result . result -- the result of result
@lordlupine бусинки глазами кнопки носом парень в очках? –
@Will Ness Вы правы ..: P –
@WillNess: Комбинатор совы с очками – amindfv