Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function gets four arguments instead of three - why doesn't this break?

Tags:

haskell

Reading "Real World Haskell", on page 95 the author provides an example:

myFoldl f z xs = foldr step id xs z
    where step x g a = g (f a x)

My question is: Why does this code compile? foldr takes only three arguments - but here, it is passed four: step, id, xs, z.

For example, this doesn't work (because sum expects one):

sum filter odd [1,2,3]

instead I must write:

sum $ filter odd [1,2,3]
like image 237
Andriy Drozdyuk Avatar asked Feb 11 '12 20:02

Andriy Drozdyuk


People also ask

How many arguments can pass through a function?

Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.

How many arguments is too many for a function?

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification - and then shouldn't be used anyway.

How many arguments can a main function have?

The main() function has two arguments that traditionally are called argc and argv and return a signed integer.

Can a function have multiple arguments?

Functions can accept more than one argument. When calling a function, you're able to pass multiple arguments to the function; each argument gets stored in a separate parameter and used as a discrete variable within the function. This video doesn't have any notes.


3 Answers

Here's the type of foldr:

Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b

Can we figure out how it becomes a four-argument function? Let's give it a try!

  1. we're giving it id :: d -> d as the second parameter (b), so let's substitute that into the type:

    (a -> (d -> d) -> (d -> d)) -> (d -> d) -> [a] -> (d -> d)
    
  2. in Haskell, a -> a -> a is the same as a -> (a -> a), which gives us (removing the last set of parentheses):

    (a -> (d -> d) -> (d -> d)) -> (d -> d) -> [a] -> d -> d
    
  3. let's simplify, by substituting e for (a -> (d -> d) -> (d -> d)) and f for (d -> d), to make it easier to read:

    e -> f -> [a] -> d -> d
    

So we can plainly see that we've constructed a four-argument function! My head hurts.


Here's a simpler example of creating an n + 1-argument function from an n-arg func:

Prelude> :t id
id :: a -> a

id is a function of one argument.

Prelude> id id id id id 5
5

But I just gave it 5 args!

like image 119
Matt Fenwick Avatar answered Oct 10 '22 19:10

Matt Fenwick


It's because of how polymorphic foldr is:

foldr :: (a -> b -> b) -> b -> [a] -> b

Here, we've instantiated b to a function type, let's call it c -> c, so the type of foldr specializes to (for example)

foldr :: (a -> (c -> c) -> (c -> c)) -> (c -> c) -> [a] -> c -> c
like image 27
Daniel Wagner Avatar answered Oct 10 '22 20:10

Daniel Wagner


foldr only takes 3 arguments

Wrong. All functions in Haskell take exactly 1 argument, and produce exactly 1 result.

foldr :: (a -> b -> b) -> b -> [a] -> b

See, foldr takes one argument (a -> b -> b), and produces 1 result: b -> [a] -> b. When you see this:

foldr step id xs z

Remember, it is just shorthand for this:

((((foldr step) id) xs) z)

This explains why this is nonsense:

sum filter odd [1,2,3]
(((sum filter) odd) [1,2,3])

sum :: Num a => [a] -> a takes a list as its input, but you gave it a function.

like image 37
Dan Burton Avatar answered Oct 10 '22 21:10

Dan Burton