Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easier way to apply multiple arguments in Haskell

Given value f with type :: Applicative f => f (a -> b -> c), What's the best way to map arguments to the inner function.

So far I've found the following:

(\x -> x a b) <$> f

(flip ($ a) b) <$> f

($ b) <$> ($ a) <$> f

I guess my question is why Haskell doesn't have a :: a -> b -> (a -> b -> c) -> c function. Or does it?

like image 983
Maciej Goszczycki Avatar asked Jan 08 '23 04:01

Maciej Goszczycki


2 Answers

The Applicative class has the <*> operator (usually pronounced "ap", and is equivalent to Control.Monad.ap for most Monads), which combined with the <$> operator (itself just an infix alias for fmap) lets you write code like

-- f :: a -> b -> c
-- fa :: Applicative f => f a
-- fb :: Applicative f => f b
f <$> fa <*> fb :: Applicative f => f c

If you need to apply pure arguments, then use the pure method of the Applicative class:

-- f :: a -> b -> c
-- a :: a
-- b :: b
f <$> pure a <*> pure b :: Applicative f => f c

An example might be

sumOfSquares :: Num a => a -> a -> a
sumOfSquares a b = a * a + b * b

> sumOfSquares <$> Just 1 <*> Just 2
Just 5
> sumOfSquares <$> Just 1 <*> Nothing
Nothing
> sumOfSquares <$> pure 1 <*> pure 2 :: Maybe Int
5
> sumOfSquares <$> readLn <*> readLn :: IO Int
1<ENTER>
2<ENTER>
5

The Applicative f => f (a -> b -> c) is being constructed by f <$> here, so if you already had something like

> let g :: IO (Int -> Int -> Int); g = undefined

Then you could just use it as

> g <*> pure 1 <*> pure 2

The <*> operator has the type

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

so if your function has type x -> y -> z, then a ~ x and b ~ y -> z, so repeated application of <*> (get it?) passes more arguments to your wrapped function.

like image 92
bheklilr Avatar answered Jan 15 '23 01:01

bheklilr


We've got

(<$>) :: Functor f => (a -> b) -> f a -> f b

But you want the opposite

(>$<) :: Functor f => f (a -> b) -> a -> f b

Which we can easily define:

(>$<) f a = ($a) <$> f

So given

f :: Functor f => f (a -> b -> c)
a :: a
b :: b

Then

f >$< a :: f (b -> c)
f >$< a >$< b :: f c

This isn't as idiomatic as <*>, but it works for all Functors, not just Applicatives, which is nice.

like image 35
rampion Avatar answered Jan 15 '23 01:01

rampion