deniok: (typed lambda)
deniok ([personal profile] deniok) wrote2009-10-16 05:09 pm
Entry tags:

Как нам посечь композицию: point-free на стероидах

На RSDN nikov последнее время пишет в point-free стиле на стероидах, используя конструкции имеющие "довольно ясный интуитивный смысл (c)". Типа
fpower = (appEndo.).(mconcat.).(.Endo).replicate
Поскольку я когда-то взвалил на себя ношу по несению point-free стиля в массы, придется писать мини-туториал.



Конструкция (. f) . g
\f g -> (. f) . g :: (d -> b) -> (a -> b -> c) -> (a -> d -> c)
Конструкция (. f) . g порождает функцию, эквивалентную g, только её второй аргумент вместо явной передачи заменен на результат вызова f.
g         :: a -> b -> c
f         :: d -> b
тогда
(. f) . g :: a -> d -> c
Диаграммой:
     d
    f|
     v
a -> b -> c
     g
В виде вызовов (с типами):
(g x::a (f z::d)::b)::c
Примеры:
-- перед отображением берем хвост списка
(. tail) . map
-- перед зиповкой увеличиваем элементы первого списка на 1
(. map succ) . zipWith
--перед свёрткой фильтруем список
(. filter (>3)) . foldr1
-- point-free определение replicate
(. repeat) . take


Конструкция (f .) . g
\f g -> (f .) . g :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
Конструкция (f .) . g порождает функцию, эквивалентную g, только её результат обрабатывается затем f.
g         :: a -> b -> c
f         :: d -> b
тогда
(f .) . g :: a -> b -> d
Диаграммой:
     g
a -> b -> c
          |f
          v
          d
В виде вызовов (с типами):
(f (g x::a y::b)::c)::d
Примеры:
-- point-free определение takeWhile
(fst .) . span
-- point-free определение mapM
(sequence .) . map
-- возвращает 0 при неудачном поиске
(fromMaybe 0 .) . find



Конструкция g . (f .)
\f g -> g . (f .) :: (d -> b) -> ((a -> b) -> c) -> ((a -> d) -> c)
Конструкция g . (f .) порождает функцию, эквивалентную g, только вместо ее функционального аргумента передается композиция f и нового функционального аргумента.
g         :: (a -> b) -> c
f         ::  d -> b
тогда
g . (f .) :: (a -> d) -> c
Диаграммой:
      d
     f|
      v
(a -> b) -> c
     g
В виде вызовов (с типами):
(g (f::d->b . h::a->d)::a->b)::c
Пример:
-- при отображении к передаваемой функции 
-- спереди прикомпозируется преобразование digitToInt
map . (digitToInt .)
-- при проверке видно, что succ вызывается 
-- до преобразования к Int (вылет на digitToInt 'G')
> (map . (digitToInt .)) succ "ABCDE"
[11,12,13,14,15]
> (map . (digitToInt .)) succ "ABCDEF"
[11,12,13,14,15,*** Exception: Char.digitToInt: not a digit 'G'



Конструкция g . (. f)
\f g -> g . (. f) :: (a -> d) -> ((a -> b) -> c) -> ((d -> b) -> c)
Конструкция g . (. f) порождает функцию, эквивалентную g, только вместо ее функционального аргумента передается композиция нового функционального аргумента и f.
g         :: (a -> b) -> c
f         ::  a -> d
тогда
g . (. f) :: (d -> b) -> c
Диаграммой:
     g
(a -> b) -> c
 |f
 v
 d
В виде вызовов (с типами):
(g (h::d->b . f::a->d)::a->b)::c
Пример:
-- при отображении к передаваемой функции 
-- сзади прикомпозируется преобразование digitToInt
map . (. digitToInt)
-- при проверке видно, что succ вызывается 
-- после преобразования к Int (нет вылета в отличие от прошлого примера)
> (map . (.digitToInt)) succ "ABCDE"
[11,12,13,14,15]
> (map . (.digitToInt)) succ "ABCDEF"
[11,12,13,14,15,16]


[identity profile] antilamer.livejournal.com 2009-10-16 01:29 pm (UTC)(link)
В последней секции должно быть Конструкция g . (. f)

[identity profile] ivan-gandhi.livejournal.com 2009-10-16 05:09 pm (UTC)(link)
Фигасе докуда прогресс дошел.

[identity profile] kodt-rsdn.livejournal.com 2009-10-16 05:14 pm (UTC)(link)
Проще было бы устроить раскрой брюк!
(((f.).g) x) y = (f.) (g x) y = (f.(g x)) y = f (g x y)
(((.f).g) x) y = (.f) (g x) y = ((g x).f) y = (g x) (f y)
((f.(g.)) x) y = f (g.x) y
((f.(.g)) x) y = f (x.g) y

Мне кажется, что можно пойти с другой стороны:
1) перечислить все формулы из 4 термов, из которых 2 связаны - и найти point-free записи для них
2) выучить самые расхожие формулы
3) в качестве образца расхожести можно взять вилки-ложки-крючочки из J

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 y
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 x g y = (f x $ g) y = ($g) (f x) y = (($g).f) x y
f x (g y) = (f x . g) y = ((.g).f) x y
f (x g) y = f (x $ g) y = (f.($g)) x y
f (x g y) = ((f.)(x $ g)) y = ((f.).($g)) x y
f (x (g y)) = (f.x.g) y = ((f.).(.g)) x y

и т.д.
Очень несложно наловчиться - и рожать point-free с сечениями, и восстанавливать.

[identity profile] ro-che.livejournal.com 2009-10-16 08:37 pm (UTC)(link)
С одной стороны, да, изврат.
С другой -- фан :) Чуть больше примеров -- и уже тянет на статью в fprog или в Monad.Reader.

[identity profile] lionet.livejournal.com 2009-10-17 01:15 am (UTC)(link)
Давай статью в fprog?

[identity profile] helvegr.livejournal.com 2009-10-18 09:23 am (UTC)(link)
А оптимизатор догадывается произвести эти трансформации?

[identity profile] migmit.vox.com (from livejournal.com) 2009-10-18 09:28 am (UTC)(link)
Про замечательную программу pointfree знаешь?

[identity profile] lebenliebhaber.livejournal.com 2009-10-23 07:23 pm (UTC)(link)
очень хороший пост

обязательно буду на него ссылаться, когда меня будут спрашивать, почему Хаскель нельзя тащить в продакшен (http://lebenliebhaber.livejournal.com/44901.html)