Привет, ребят. Хочу разукрасить вашу беседу следующим вопросом.
Пусть у нас будет некоторая функция, которая принимает три аргумента.
p x y z = f (g x y) z
В какой-то момент мы осознали, что не хотим видеть какие-либо аргументы в нашем коде, поэтому пробуем эту функцию модифицировать (на псевдокоде).
p = \x -> \y -> \z -> f (g x y) z # z можно опустить
= \x -> \y -> f (g x y) # вынесем y с помощью композии и явного приоритета
= \x -> \y -> (f . (g x)) y # y можно опустить
= \x -> f . (g x) # композицию запишем в префиксном виде
= \x -> ((.) f) (g x) # попробуем вынести x
= ((.) f) . g # эм, что?
Вопрос в последних двух выражениях, не очень хорошо понимаю, что происходит. Попробуете объяснить?
p = \x -> ((.) f) (g x)
= ((.) f) . (\x -> g x)
= ((.) f) . g
Для того, чтобы убедить себя в верности этих эзотерических манипуляций можно воспользоваться quickcheck:
import Test.QuickCheck
import Test.QuickCheck.All
p0 :: Int -> Int -> Int -> Int
p0 x y z = f (g x y) z
p1 :: Int -> Int -> Int -> Int
p1 x y = f (g x y)
p2 :: Int -> Int -> Int -> Int
p2 x = f . (g x)
p3 :: Int -> Int -> Int -> Int
p3 x = ((.) f) (g x)
p4 :: Int -> Int -> Int -> Int
p4 = ((.) f) . (\x -> g x)
p5 :: Int -> Int -> Int -> Int
p5 = ((.) f) . g
f :: Int -> Int -> Int
f = (+)
g :: Int -> Int -> Int
g = (*)
test = do
quickCheck (\x y z -> p0 x y z == p1 x y z)
quickCheck (\x y z -> p0 x y z == p2 x y z)
quickCheck (\x y z -> p0 x y z == p3 x y z)
quickCheck (\x y z -> p0 x y z == p3 x y z)
quickCheck (\x y z -> p0 x y z == p4 x y z)
quickCheck (\x y z -> p0 x y z == p5 x y z)
Можно ещё от скобок избавиться:
p6 :: Int -> Int -> Int -> Int
p6 = (. f) . g
На тему сечений композиции есть ещё вот такая интересная статья
Вы что шутите что ли, какой квикчек. Просто в последнем шаге \x -> ((.) f) (g x) обозначьте ((.) f) за h. Получится: \x -> h (g x). Какие есть сомнения, что это эквивалентно h . g?