I keep reusing lambda expressions such as
\x -> (f x, g x)
where I apply the same input to two functions and encapsulate the result in a pair. I can write a function capturing this
combine :: (a -> b) -> (a -> c) -> a -> (b,c)
combine f g x = (f x, g x)
Now the above lambda expression is just combine f g
. I have two questions.
Control.Arrow
has the function (&&&)
for this. It has a "more general" type, which unfortunately means that Hoogle doesn't find it (maybe this should be considered a bug in Hoogle?).
You can usually figure this sort of thing automatically with pointfree
, which lambdabot
in #haskell
has as a plugin.
For example:
<shachaf> @pl combine f g x = (f x, g x)
<lambdabot> combine = liftM2 (,)
Where liftM2
with the (r ->)
instance of Monad
has type (a -> b -> c) -> (r -> a) -> (r -> b) -> r -> c
. Of course, there are many other ways of writing this point-free, depending on what primitives you allow.
I'm interested to know if there is a standard library function that does this that I just can't find.
It's easy to miss because of the type class, but look at Control.Arrow
. Plain Arrow
s can't be curried or applied, so the Arrow
combinators are pointfree by necessity. If you specialize them to (->)
, you'll find the one you want is this:
(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c')
There are other, similar functions, such as the equivalent operation for Either
, which specialized to (->)
looks like this:
(|||) :: (a -> c) -> (b -> c) -> Either a b -> c
Which is the same as either
.
Out of curiosity, I'd like to rewrite this function in point-free style, but I'm having a lot of trouble with it.
Since you're duplicating an input, you need some way of doing that pointfree--the most common way is via the Applicative
or Monad
instance for (->)
, for example \f g ->
. This is essentially an implicit, inline (,)
<$> f <*> gReader
monad, and the argument being split up is the "environment" value. Using this approach, join f x
becomes f x x
, pure
or return
become const
, fmap
becomes (.)
, and (<*>)
becomes the S combinator \f g x -> f x (g x)
.
There are actually quite a few ways of doing this. The most common way is to use the (&&&)
function from Control.Arrow
:
f &&& g
However, often you have more functions or need to pass the result to another function, in which case it is much more convenient to use applicative style. Then
uncurry (+) . (f &&& g)
becomes
liftA2 (+) f g
As noted this can be used with more than one function:
liftA3 zip3 f g h
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With