6

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.

duplode
34.5k7 gold badges88 silver badges159 bronze badges
asked Dec 3, 2016 at 12:27
4
  • Wouldn't it make more sense to take x :: a as the last argument? Commented Dec 3, 2016 at 12:36
  • @leftaroundabout oh yeah, it does. Thanks for pointing that out. But this is just pseudocode to explain my issue. Its just that I have a lot of functions and types that use a pattern similar to this and I'm wondering if theres a better way to go about doing this. Commented Dec 3, 2016 at 12:40
  • 2
    Your wfmap declares 5 arguments in the type, but only 4 in the definition. I think you just have an extra argument of type a in the type. Commented Dec 3, 2016 at 13:07
  • 1
    Functor f => (b -> f b) -> a -> f a is also called Lens' a b and is the centerpiece of the lens library. Your example could end up looking like wfmap x u w g = (iso u w %%~ g) x, though of course you wouldn't give wfmap its own name. Commented Dec 3, 2016 at 15:50

1 Answer 1

7

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.

answered Dec 3, 2016 at 15:27
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this! :)

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.