tags:

views:

106

answers:

2

I'm reviewing Haskell: The Craft of Functional Programming, however the type signatures on page 356 have thrown me for a loop.

Here is a simple one:

succeed :: b -> Parse a b
succeed val inp = [( val, inp )]

How can that be b -> Parse a b, if

succeed Int -> Int
succeed a = a

and,

succeed Int -> Int -> Int
succeed a b = a + b

The amount of arguments your taking has to be in the type declaration right? How can you take val, and imp, if your type declaration only has one type variable: succeed :: b -> Parse a b is supposed to read, take one variable of type a and return a type of Parse a b, not take two variables... where is inp permitted?

+5  A: 

Because Parse is a type synonym with -> in its expansion. Example:

type Foo = Int -> Int

succeed :: Int -> Foo
succeed a b = a + b
keegan
I looked dazed at this for a while, the explicit mention of a synonym with `->` was certainly helpful. I understand what you mean now, this seems to rather obscure the clarity of the type system -- could you or someone else comment on this affect..
Evan Carroll
@Evan: Writing `succeed`'s type with that shortcut makes it explicit you probably want to apply that function to two arguments. You're supposed to apply it to one argument so it produces a parser (aka function). It could have been defined with only with argument on the left side, like this `succeed val = \inp -> [(val, inp)]`, i.e., takes an argument `val` and produces a *parser* (aka function) that takes `inp` and produces that list. It just happens that a parser is actually a function. Because you can easily compose functions, you can easily compose parsers written in this style.
Martinho Fernandes
I wish the book was this explicit about what was happening
Evan Carroll
A: 

In Haskell all functions take exactly one argument and produce exactly one result. When have a type like this a -> b -> c you have the same as a -> (b -> c). So, instead of a two-argument function, you actually have a function that when given an argument produces another function that takes the second argument and produces the final result.

When you apply a function to two arguments, like this f x y, it's the same as (f x) y. You're actually applying the function that results from f x to the second argument y. From this behavior you get "partial application" for free.

There is a pair of functions in the Prelude named curry and uncurry that let you convert between functions written in this style and functions that take two arguments directly as a pair:

uncurry :: (a -> b -> c) -> ((a,b) -> c)
curry :: ((a,b) -> c) -> (a -> b -> c)

f  :: a -> b -> c
f = ...
f' :: (a,b) -> c
f' = uncurry f

I don't know how Parse was defined, but if you define it as a synonym to a function type (something common with parsers), you get that kind of behavior.

Martinho Fernandes