Use cases for functor/applicative/monad instances for functions

后端 未结 3 1214
抹茶落季
抹茶落季 2021-02-01 21:07

Haskell has Functor, Applicative and Monad instances defined for functions (specifically the partially applied type (->) a

相关标签:
3条回答
  • 2021-02-01 21:23

    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

    0 讨论(0)
  • 2021-02-01 21:32

    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.

    0 讨论(0)
  • 2021-02-01 21:33

    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
    
    0 讨论(0)
提交回复
热议问题