简体   繁体   中英

Haskell: understanding partial application?

I'm reading the LYAH chapter on applicative functors, and I don't seem to understand the following example:

ghci> :t fmap (++) (Just "hey")  
fmap (++) (Just "hey") :: Maybe ([Char] -> [Char])

But when I look at this:

ghci> :t (++)
(++) :: [a] -> [a] -> [a]
ghci> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b

I do understand how something like (*3) or (++"this") fits into the (a -> b) type, but I just can't see how [a] -> [a] -> [a] fits into (a -> b) ?

The key is that -> associates to the right, so a type like a -> b -> c is really a -> (b -> c) . So [a] -> [a] -> [a] fits into c -> d by setting c ~ [a] and d ~ [a] -> [a] . You can view a function [a] -> [a] -> [a] either as a function of 2 arguments that returns a result of type [a] , or a function of 1 argument that returns a result of type [a] -> [a] .

The thing to realise is that the b in a -> b doesn't have to be a scalar - it can be a function.

[a] -> [a] -> [a] can be thought of as [a] -> ([a] -> [a]) , so b is [a] -> [a]

Putting the stuff side-by-side as usual,

fmap :: Functor f => ( a    ->      b      )   ->      f a        ->   f b
fmap                       (++)                    (Just "hey")   ::   f b
(++) ::               [c]   -> ([c] -> [c])

So,

a ~ [c]  ,    b ~ ([c] -> [c])  ,    f ~ Maybe  ,    a ~ [Char]  ,   c ~ Char

f b ~ Maybe b ~ Maybe ([c] -> [c]) ~  Maybe ([Char] -> [Char])

No thinking is involved here. Unification of types is a mechanical process.


And to answer your specific question (paraphrased), " how [c] -> [c] -> [c] can be matched with a -> b " , here goes:

  • Omitting parentheses in type signatures is evil (when teaching Haskell to newbies)
  • In Haskell, there are no binary functions . Every function is unary.
  • Hence (as others mentioned already), arrows in type signatures associate to the right .

It is simple really :-). let me add a simple parenthesis:

[a]->[a]->[a] is like [a]->([a]->[a])

So it fits in a->b by replacing a by [a] and b by [a]->[a] . You give a string to ++ and you get a function of type string->string in return

fmap (++) (Just "hey") is a maybe monad holding a function which prefix the string "hey" to another string. It is indeed of type Maybe ([Char] -> [Char])

Consider the definition of fmap for the Maybe type.

fmap f (Just x) = Just (f x)

which for your example looks like

fmap (++) (Just "Hey") = Just ("Hey" ++) :: Maybe ([Char] -> [Char])

As fmap should, you have simply lifted the (++) function inside the Maybe container and applied it to the contents.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM