Search code examples
haskellfunctor

Isomorphic `fmap` in Haskell


Does such a thing exist in Haskell's Prelude?

wfmap :: Functor f
      => a
      -> (a -> b)
      -> (b -> a)
      -> (b -> f b)
      -> f a
wfmap x u w g = fmap (w) (g (u x))

In a project I'm working on, I often found myself 'converting' a type to another, process it and 'converting' it back.


Solution

  • Reordering the arguments, as leftaroundabout suggests, allows for a tidier definition:

    wfmap :: Functor f => (a -> b) -> (b -> a) -> (b -> f b) -> a -> f a
    wfmap u w g = fmap w . g . u
    

    As for library support, lens provides nifty support for isomorphisms. A bit more broadly, as Gurkenglas notes...

    Functor f => (b -> f b) -> a -> f a is also called Lens' a b and is the centerpiece of the lens library.

    Without diving into the details of how and why that works, one consequence is that your function might be defined as:

    wfmap :: Functor f => (a -> b) -> (b -> a) -> (b -> f b) -> a -> f a
    wfmap u w g = (iso u w) g
    

    Or even:

    wfmap :: Functor f => (a -> b) -> (b -> a) -> (b -> f b) -> a -> f a
    wfmap = iso
    

    wfmap is just (a specialised version of) iso, which gives out a function which can be used to convert an b -> f b function on the isomorphism "destination" to an a -> f a one on the isomorphism "source".

    It is also worth mentioning mapping, which can be used for the somewhat different purpose of applying fmap on the other side of an isomorphism:

    GHCi> :t \u w g -> over (mapping (iso u w)) (fmap g)
    \u w g -> over (mapping (iso u w)) (fmap g)
      :: Functor f => (s -> a) -> (b -> t) -> (a -> b) -> f s -> f t
    GHCi> :t \u w g -> under (mapping (iso u w)) (fmap g)
    \u w g -> under (mapping (iso u w)) (fmap g)
      :: Functor f => (s -> a) -> (b -> a1) -> (a1 -> s) -> f b -> f a
    

    Finally, note that iso u w can be replaced by any Iso you might find in the libraries or have predefined elsewhere.