Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generalized `fold` or how to perform `fold` and `map` at a time

(Apology by the title, I can't do better)

My question is to find some generalized struct or "standard" function to perform the next thing:

xmap :: (a -> b) -> f a -> g b

then, we can map not only elements, by also the entire struct.

Some (not real) example

xmap id myBinaryTree :: [a]

at the moment, I must to do a explicit structure conversor (typical fromList, toList) then

toList . fmap id   -- if source struct has map
fmap id . fromList -- if destination struct has map

(to perform toStruct, fromStruct I use fold).

Exists some way to generalize to/from structs? (should be) Exists that function (xmap)?

Thank you!! :)

like image 998
josejuan Avatar asked Jul 24 '13 13:07

josejuan


2 Answers

As f and g are functors, a natural transformation is what you're looking for (see also You Could Have Defined Natural Transformations). So a transformation like

f :~> g = forall a. f a -> g a 

is needed to create xmap which is then just

xmap :: (a -> b) -> (f :~> g) -> (f a -> g b)
xmap f n = map f . n

You still need to define types of (f :~> g), but there' not a general way of doing that.

like image 119
J. Abrahamson Avatar answered Nov 06 '22 07:11

J. Abrahamson


I'd like to add to tel's answer (I got my idea only after reading it) that in many cases you can make general natural transformation that will work similarly to foldMap. If we can use foldMap, we know that f is Foldable. Then we need some way how to constructs elements of g a and combine them together. We can use Alternative for that, it has all we need (pure, empty and <|>), although we could also construct some less general type class for this purpose (we don't need <*> anywhere).

{-# LANGUAGE TypeOperators, RankNTypes #-}
import Prelude hiding (foldr)
import Control.Applicative
import Data.Foldable

type f :~> g = forall a. f a -> g a

nt :: (Functor f, Foldable f, Alternative g) => f :~> g
nt = foldr ((<|>) . pure) empty

Then using tel's xmap

xmap :: (a -> b) -> (f :~> g) -> (f a -> g b)
xmap f n = map f . n

we can do things like

> xmap (+1) nt (Just 1) :: [Int]
[2]
like image 4
Petr Avatar answered Nov 06 '22 08:11

Petr