Моя предпочтительная реализация для этого
fmap . fmap :: (Functor f, Functor f1) => (a -> b) -> f (f1 a) -> f (f1 b)
Хотя бы потому, что это довольно легко запомнить.
При создании экземпляров f и f1 для (->) c
и (->) d
соответственно вы получаете тип
(a -> b) -> (c -> d -> a) -> c -> d -> b
это тип
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
но немного проще отказаться от версии fmap . fmap
и она обобщает на другие функторы.
Иногда это пишется fmap fmap fmap
, но записывается как fmap . fmap
, его можно легко расширить, чтобы разрешить больше аргументов.
fmap . fmap . fmap
:: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))
fmap . fmap . fmap . fmap
:: (Functor f, Functor g, Functor h, Functor i) => (a -> b) -> f (g (h (i a))) -> f (g (h (i b))
* * Тысяча двадцать-одина и т.д.
В общем случае fmap
, составленный сам с собой n раз, может использоваться для fmap
n уровней глубины!
И поскольку функции образуют Functor
, это обеспечивает верстку для n аргументов.
Для получения дополнительной информации см. Conal Elliott Semantic Editor Combinators .