When reading this article I found example of generating Fibonacci sequence with unfoldr
function:
fibs = unfoldr (\(a,b) -> Just (a,(b,a+b))) (0,1)
But when I looked at documentation I found that lambda in unfoldr
function takes only one argument b
:
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
Examples in the doc also demonstrate only one argument usage:
unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
So, I'm interested how can it be applied to two arguments as in Fibonacci example?
This is your function:
GHCi> let foo = \(a,b) -> Just(a,(b,a+b))
While we can certainly interpret it in our heads as a function of two arguments, as far as Haskell is concerned it only takes one argument...
foo :: Num t => (t, t) -> Maybe (t, (t, t))
... a pair of type (t, t)
for some t
in the Num
class. All that is going on in your example, then, is that the b
in unfoldr
's signature is being replaced by Num t => (t, t)
, and a
by Num t => t
, resulting in:
Num t => ((t, t) -> Maybe (t, (t, t))) -> (t, t) -> [t]
Some extra evidence of that:
GHCi> :t unfoldr (\(a,b) -> Just (a,(b,a+b)))
unfoldr (\(a,b) -> Just (a,(b,a+b))) :: Num a => (a, a) -> [a]
With your immediate question answered, here is a little digression. Another way of passing two arguments to a Haskell function is passing them separately, as opposed to in a pair:
GHCi> let foo2 a b = Just(a,(b,a+b))
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
Obviously, if you still have foo
around you don't even need to rewrite the implementation:
GHCi> let foo2 a b = foo (a, b)
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
In fact, this transformation -- which in jargon is called currying -- is always possible, and there is even a function for doing that...
GHCi> let foo2 = curry foo
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
... as well as another one for going in the opposite direction:
GHCi> :t uncurry foo2
uncurry foo2 :: Num t => (t, t) -> Maybe (t, (t, t))
What might be surprising in all that, however, is how even foo2
, which looks even more like a function of two arguments than foo
, still is a function of one argument! The trick is that a signature like that of foo2
...
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
... has some omitted, optional parentheses. The omission conceals the fact that the function arrow, ->
, is right-associative. If we add them, we get:
foo2 :: Num t => t -> (t -> Maybe (t, (t, t)))
That is, foo2
takes one argument (that is, our first argument) and returns another function which also takes one argument (that is, our second argument).
The main takeaway, then, is that all Haskell functions take just one argument. Fortunately, it is very easy to write functions that work as if they took two arguments. More often than not, that is done by writing the function in a curried way (that is, as a function like foo2
that returns another function), but sometimes it is necessary or convenient to pass multiple values in a pair, as you had to do in your example.