I am new to Haskell and I am reading the book "Real World Haskell". In the Chapter 4 of the book the author asks as an exercise to rewrite the groupBy function using fold. One of the readers of the book (Octavian Voicu ) gave the following solution:
theCoolGroupBy :: (a -> a -> Bool) -> [a] -> [[a]]
theCoolGroupBy eq xs = tail $ foldr step (\_ -> [[]]) xs $ (\_ -> False)
where step x acc = \p -> if p x then rest p else []:re开发者_开发百科st (eq x)
where rest q = let y:ys = acc q in (x:y):ys
My question is simple: I know that foldr takes 3 arguments: a function, an initial value and a list. But in the second line of the code foldr takes 4 arguments. Why this happens? Thank you.
In this situation, I think it is best to look at the type signature of foldr
:
foldr :: (a -> b -> b) -> b -> [a] -> b
and to match that to the expression we have (with added parenthesis for clarity):
(foldr step (\_ -> [[]]) xs) (\_ -> False)
The second argument of foldr
is the same type as its result. In this case the second argument is a function. In this case, this means that the foldr
expression with 3 arguments will be a function.
What you see to be the 4th argument of the foldr function could also be thought of as the 1st argument of the foldr result!
All functions in Haskell take just one argument. When we have a function with type a -> b -> c
, it is just a shorter way to write a -> (b -> c)
, i.e. a function, which takes one argument and produces a function which takes another argument. See Currying for more information.
In this case, see the @sepp2k's answer. foldr
produces a function and it needs another ("the 4th") argument.
In this case foldr
is used to build up a function. (\_ -> False)
is the argument to that function.
Scott's answer is correct, the result of the foldr
is a function, so this is why it seems that foldr
takes 4 arguments. The foldr
functions does take 3 arguments (function, base, list):
*Main> :type foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
I'll just give here an example that is less complex:
inc :: Int -> (Int -> Int)
inc v = \x -> x + v
test = inc 2 40 -- output of test is 42
In the above code, inc
takes one argument, v
, and returns a function that increments its argument by v
.
As we can see below, the return type of inc 2
is a function, so its argument can simply be added at the end:
*Main> :type inc
inc :: Int -> Int -> Int
*Main> :type inc 2
inc 2 :: Int -> Int
*Main> :type inc 2 40
inc 2 40 :: Int
Parentheses could be used to emphasize that the return value is a function, but functionally it is identical to the above code:
*Main> (inc 2) 40
42
PS: I'm the author of the original comment :)
精彩评论