Это один из тех изящных случаев, когда я думаю, что проще сначала понять более общий случай, а затем подумать о конкретном случае. Итак, давайте подумаем о функторах. Мы знаем, что функторы позволяют отображать функции на структуру.
class Functor f where
fmap :: (a -> b) -> f a -> f b
Но что, если у нас есть два слоя функтора? Например, список списков? В этом случае мы можем использовать два слоя fmap
>>> let xs = [[1,2,3], [4,5,6]]
>>> fmap (fmap (+10)) xs
[[11,12,13],[14,15,16]]
Но шаблон f (g x)
точно такой же, как (f . g) x
, поэтому мы могли бы написать
>>> (fmap . fmap) (+10) xs
[[11,12,13],[14,15,16]]
Какой тип fmap . fmap
?
>>> :t fmap.fmap
:: (Functor g, Functor f) => (a -> b) -> f (g a) -> f (g b)
Мы видим, что он отображает два слоя функтора, как мы и хотели. Но теперь помните, что (->) r
— это функтор (тип функций из r
, который вы могли бы предпочесть читать как (r ->)
), и его экземпляр функтора — это
instance Functor ((->) r) where
fmap f g = f . g
Для функции fmap
— это просто композиция функций! Когда мы составляем два fmap
, мы отображаем два уровня функционального функтора. Сначала у нас есть что-то типа (->) s ((->) r a)
, что эквивалентно s -> r -> a
, и в итоге мы получим что-то типа s -> r -> b
, поэтому тип (.).(.)
должен быть
(.).(.) :: (a -> b) -> (s -> r -> a) -> (s -> r -> b)
которая берет свою первую функцию и использует ее для преобразования вывода второй (с двумя аргументами) функции. Так, например, функция ((.).(.)) show (+)
— это функция двух аргументов, которая сначала складывает свои аргументы, а затем преобразует результат в String
, используя show
:
>>> ((.).(.)) show (+) 11 22
"33"
Тогда возникает естественное обобщение для размышлений о более длинных цепочках fmap
, например
fmap.fmap.fmap ::
(Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))
который отображает три слоя функтора, что эквивалентно составлению функции с тремя аргументами:
(.).(.).(.) :: (a -> b) -> (r -> s -> t -> a) -> (r -> s -> t -> b)
Например
>>> import Data.Map
>>> ((.).(.).(.)) show insert 1 True empty
"fromList [(1,True)]"
который вставляет значение True
в пустую карту с ключом 1
, а затем преобразует вывод в строку с show
.
Эти функции могут быть полезны в целом, поэтому иногда вы видите их определенными как
(.:) :: (a -> b) -> (r -> s -> a) -> (r -> s -> b)
(.:) = (.).(.)
так что вы можете написать
>>> let f = show .: (+)
>>> f 10 20
"30"
Конечно, можно дать более простое и точное определение (.:)
.
(.:) :: (a -> b) -> (r -> s -> a) -> (r -> s -> b)
(f .: g) x y = f (g x y)
что может помочь несколько демистифицировать (.).(.)
.
person
Chris Taylor
schedule
11.07.2013
(.)
на самом деле принимает один аргумент и возвращает функцию, которая принимает один аргумент. - person Wes   schedule 11.07.2013(.) f g a = f (g a)
- person Ingo   schedule 11.07.2013(.) (+1) (*2) 3
работает :) - person Wes   schedule 11.07.2013(return.return)
, сreturn = (.)
чтобы получить общее представление о таких вещах. - person AndrewC   schedule 16.07.2013