22

Haskell has Functor, Applicative and Monad instances defined for functions (specifically the partially applied type (->) a) in the standard library, built around function composition.

Understanding these instances is a nice mind-bender exercise, but my question here is about the practical uses of these instances. I'd be happy to hear about realistic scenarios where folks used these for some practical code.

4
  • 8
    The Reader monad is basically just a newtype wrapper around (->). Commented Oct 8, 2017 at 12:50
  • I use them all the time. Probably you do it yourself without knowing: . is just fmap. Commented Oct 8, 2017 at 13:07
  • @Bergi: sure, I guess the question is why use them instead of just using (.) Commented Oct 8, 2017 at 13:14
  • 2
    @Bergi Composition exists independently of the Functor instance; it is more accurate to say that fmap is just .. Commented Oct 8, 2017 at 14:12

3 Answers 3

7

A common pattern that involves Functor and Applicative instances of functions is for example (+) <$> (*2) <*> (subtract 1). This is particularly useful when you have to feed a series of function with a single value. In this case the above is equivalent to \x -> (x * 2) + (x - 1). While this is very close to LiftA2 you may extend this pattern indefinitely. If you have an f function to take 5 parameters like a -> a -> a -> a -> a -> b you may do like f <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) and feed it with a single value. Just like in below case ;

Prelude> (,,,,) <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) $ 10 (12.0,20.0,11.0,7.0,5.0) 

Edit: Credit for a re-comment of @Will Ness for a comment of mine under another topic, here comes a beautiful usage of applicative over functions;

Prelude> let isAscending = and . (zipWith (<=) <*> drop 1) Prelude> isAscending [1,2,3,4] True Prelude> isAscending [1,2,5,4] False 
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the input. So the 5-tuple example you posted. It can be rewritten as (\x -> (x+2, x*2, x+1, x-3, x/2)) 10 -- the advantage of the applicative here is that there's no need to repeat x?
@Eli Bendersky The main difference is, in the applicative form if you know how many parameters the main function takes, the whole thing is functionally decomposable (that you can dynamically compose the parameters the way you like) while in lambda form you have to have a solid lambda function at hand.
are you saying that we already have a function, like (,,,,) and don't have to write the lambda by hand?
I guess I can sort-of see what you are trying to say, but it's hard for me to envision without a concrete use case :)
@Eli Bendersky I mean the lambda function (\x -> (x+2, x*2, x+1, x-3, x/2)) would be hard wired while in applicative style it wouldn't. One can make up a new function with the applicative one to change the "mapping" functions dynamically. A concrete use case... is hard to come up with spontaneously but this is a good knowledge to have in mind.
|
4

Sometimes you want to treat functions of the form a -> m b (where m is an Applicative) as Applicatives themselves. This often happens when writing validators, or parsers.

One way to do this is to use Data.Functor.Compose, which piggybacks on the Applicative instances of (->) a and m to give an Applicative instance for the composition:

import Control.Applicative import Data.Functor.Compose type Star m a b = Compose ((->) a) m b readPrompt :: Star IO String Int readPrompt = Compose $ \prompt -> do putStrLn $ prompt ++ ":" readLn main :: IO () main = do r <- getCompose (liftA2 (,) readPrompt readPrompt) "write number" print r 

There are other ways, like creating your own newtype, or using ready-made newtypes from base or other libraries.

Comments

0

here an application of the bind function that I used for solving the Diamond Kata. Take a simple function that mirrors its input discarding the last element

mirror :: [a] -> [a] mirror xs = xs ++ (reverse . init) xs 

let's rewrite it a bit

mirror xs = (++) xs ((reverse . init) xs) mirror xs = flip (++) ((reverse . init) xs) xs mirror xs = (reverse . init >>= flip (++)) xs mirror = reverse . init >>= flip (++) 

Here is my complete implementation of this Kata: https://github.com/enolive/exercism/blob/master/haskell/diamond/src/Diamond.hs

1 Comment

Since ma >>= f equals flip f <*> ma you can limit yourself to the Applicative instance of functions, i.e. mirror = (++) <*> reverse . init. Not using (>>=) and the Monad instance of functions seems to be reasonable.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.