![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
На RSDN nikov последнее время пишет в point-free стиле на стероидах, используя конструкции имеющие "довольно ясный интуитивный смысл (c)". Типа
Конструкция (. f) . g
Конструкция (f .) . g
Конструкция g . (f .)
Конструкция g . (. f)
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]
no subject
Date: 2009-10-18 10:26 am (UTC)no subject
Date: 2009-10-18 10:50 am (UTC)На моём маленьком примере работает:
Исходник:
Core:
Как можно видеть, Core-представление обоих функций идентично.
no subject
Date: 2009-10-18 01:36 pm (UTC)