More fun with applicative functors

前端 未结 5 635
我在风中等你
我在风中等你 2020-12-28 17:48

Earlier I asked about translating monadic code to use only the applicative functor instance of Parsec. Unfortunately I got several replies which answered the question I lite

5条回答
  •  我在风中等你
    2020-12-28 18:37

    I'd like to add/reword a couple things to the very helpful existing answers:

    Applicatives are "static". In pure f <*> a <*> b, b does not depend on a, and so can be analyzed statically. This is what I was trying to show in my answer to your previous question (but I guess I failed -- sorry) -- that since there was actually no sequential dependence of parsers, there was no need for monads.

    The key difference that monads bring to the table is (>>=) :: Monad m => m a -> (a -> m b) -> m a, or, alternatively, join :: Monad m => m (m a). Note that whenever you have x <- y inside do notation, you're using >>=. These say that monads allow you to use a value "inside" a monad to produce a new monad, "dynamically". This cannot be done with an Applicative. Examples:

    -- parse two in a row of the same character
    char             >>= \c1 ->
    char             >>= \c2 ->
    guard (c1 == c2) >>
    return c1
    
    -- parse a digit followed by a number of chars equal to that digit
    --   assuming: 1) `digit`s value is an Int,
    --             2) there's a `manyN` combinator
    -- examples:  "3abcdef"  -> Just {rest: "def", chars: "abc"}
    --            "14abcdef" -> Nothing
    digit        >>= \d -> 
    manyN d char 
    -- note how the value from the first parser is pumped into 
    --   creating the second parser
    
    -- creating 'half' of a cartesian product
    [1 .. 10] >>= \x ->
    [1 .. x]  >>= \y ->
    return (x, y)
    

    Lastly, Applicatives enable lifted function application as mentioned by @WillNess. To try to get an idea of what the "intermediate" results look like, you can look at the parallels between normal and lifted function application. Assuming add2 = (+) :: Int -> Int -> Int:

    -- normal function application
    add2 :: Int -> Int -> Int
    add2 3 :: Int -> Int
    (add2 3) 4 :: Int
    
    -- lifted function application
    pure add2 :: [] (Int -> Int -> Int)
    pure add2 <*> pure 3 :: [] (Int -> Int)
    pure add2 <*> pure 3 <*> pure 4 :: [] Int
    
    -- more useful example
    [(+1), (*2)]
    [(+1), (*2)] <*> [1 .. 5]
    [(+1), (*2)] <*> [1 .. 5] <*> [3 .. 8]
    

    Unfortunately, you can't meaningfully print the result of pure add2 <*> pure 3 for the same reason that you can't for add2 ... frustrating. You may also want to look at the Identity and its typeclass instances to get a handle on Applicatives.

提交回复
热议问题