Как понимать сечения композиции

Привет, ребят. Хочу разукрасить вашу беседу следующим вопросом.

Пусть у нас будет некоторая функция, которая принимает три аргумента.

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                       # эм, что?

Вопрос в последних двух выражениях, не очень хорошо понимаю, что происходит. Попробуете объяснить?

Привет, @Goga .

Для наглядности можно вставить ещё один шаг:

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

На тему сечений композиции есть ещё вот такая интересная статья

3 лайка

Вы что шутите что ли, какой квикчек. Просто в последнем шаге \x -> ((.) f) (g x) обозначьте ((.) f) за h. Получится: \x -> h (g x). Какие есть сомнения, что это эквивалентно h . g?

Вот сюда можно приходить, чтобы получать эти пугающие заклинания.

2 лайка

Так даже лучше, спасибо.

Выходит, что и префиксная запись не нужна, вместо ((.) f) лучше написать (f .), целая пара скобочек!!1

А вот другой пример:

mf = (. map) . (.) . filter

Если честно, я такое вообще никак не могу взять и на глаз развернуть в нормальный вид:

mf criteria operator list = filter criteria (map operator list)

Смириться или стоит-таки научиться с таким разбираться?

Я композицию функций с несколькими аргументами читать не умею, но как-то выживаю…