Haskell has Functor
, Applicative
and Monad
instances defined for functions (specifically the partially applied type (->) a
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
Sometimes you want to treat functions of the form a -> m b
(where m
is an Applicative
) as Applicative
s 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.
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