问题
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.
回答1:
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
回答2:
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.
回答3:
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
来源:https://stackoverflow.com/questions/46631242/use-cases-for-functor-applicative-monad-instances-for-functions