lovasoa
lovasoa

Reputation: 6855

Apply a function to every arguments in haskell

I sometimes find myself in a case where I need to create a function that maps a different function to two values, and then combines them together. How to do that without using a lambda ?

My functions are:

f :: a -> b
g :: c -> d
combine :: b -> d -> e

The question is: how to write \x y -> combine (f x) (g y) without using a lambda ?

Upvotes: 3

Views: 222

Answers (2)

Julia Path
Julia Path

Reputation: 2366

If f = g you can use on in Data.Function. Otherwise there is no combinator for that in base. A stackage search for the appropriate type reveals biSp from concatenative.

biSp :: (a -> c) -> (b -> d) -> (c -> d -> e) -> a -> b -> e

However if that is the only function you will be using from that package, you may not want to depend on a package just for that.

Other than that you can use the following which would be at least marginally shorter, but not really more readable.

(. g) . combine . f

Or using dimap in Data.Profunctor from profunctors. While this also requires another package, it is likely that you are already depending on profunctors indirectly anyway, because you are using lens or some package depending on lens.

dimap f (. g) combine

This can easily be extended to three arguments. The following all do the same and are ordered from shortest to most structured and with the last two it is especially easy to extend them to any number of arguments.

dimap f (dimap g (. h)) combine3
(dimap f . dimap g) (. h) combine3
(dimap f . dimap g . dimap h) id combine3

If you wonder what this dimap does. This is a great tutorial: I love profunctors. They're so easy.

Or just write your own biSp.

Upvotes: 7

duplode
duplode

Reputation: 34398

An alternative pointfree spelling involves handling the arguments as a pair by using curry, uncurry and either (***) (from Control.Arrow) or bimap (from Data.Bifunctor):

GHCi> :t \combine f g -> curry (uncurry combine . (f *** g))
\combine f g -> curry (uncurry combine . (f *** g))
  :: (a1 -> b1 -> c) -> (a -> a1) -> (b -> b1) -> a -> b -> c

Note that, unlike jpath's suggestions, this isn't even shorter than the non-pointfree version.

Upvotes: 4

Related Questions