Определите правильный функтор для обертывания ваших кортежей.
data Three a = Three {getThree :: (a, a, a)} deriving (Show, Functor)
Если вы не хотите использовать расширение DeriveFunctor
, определение простое:
instance Functor Three where
fmap f (Three (x, y, z)) = Three (f x, f y, f z)
ТогдаВы можете просто определить plusOne
как
>>> plusOne = let f = getThree . fmap (+1) . Three in fmap (fmap f)
, где f
- это функция, которая оборачивает 3-кортеж, отображает (+1)
на каждый элемент и разворачивает результат.Это сопоставляется с вашим списком списков:
> x = [[(1, 2, 3), (4,5,6)], [(7,8,9)]]
> plusOne x
[[(2,3,4),(5,6,7)],[(8,9,10)]]
Вы также можете использовать Data.Functor.Compose
, чтобы исключить один из уровней fmap
(или, по крайней мере, скрыть его за другим набором именчтобы разбить монотонность):
> getCompose . fmap (getThree . fmap (+1) . Three) . Compose $ x
[[(2,3,4),(5,6,7)],[(8,9,10)]]
Мы применили один и тот же шаблон обертывания / создания / распаковки дважды.Мы можем абстрагироваться от вспомогательной функции
-- wrap, map, and unwrap
wmu pre post f = post . fmap f . pre
plusOne = wmu Compose getCompose $ wmu Three getThree $ (+1)
Можно заметить сходство между wmu
и dimap
(специализируется на (->)
):
wmu pre post = dimap pre post . fmap
Все будет еще проще, если вы в первую очередь сможете заменить универсальный кортеж на пользовательский тип продукта.
data Triplet a = Triplet a a a
-- Can be derived as well
instance Functor Triplet where
fmap f (Triplet x y z) = Triplet (f x) (f y) (f z)
plusOne :: [[Triplet Int]] -> [[Triplet Int]]
plusOne = fmap (fmap (fmap (+1)))