Yep, another dollar-sign question. I'm sorry... (I used the search function!)
My professor for the course functional programming told us that the dollar sign 'kinda adds an opening parenthese and then a closing one at the end' (it's very roughly described here in more or less the same manner). So
fibs = 0 : 1 : zipWith (+) fibs $ tail fibs
should be equivalent to
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Well, it isn't. The second thing compiles fine, the first thing gives an error:
jkjj.hs:1:8:
Couldn't match expected type `[a1] -> [a1]' with actual type `[a0]'
The first argument of ($) takes one argument,
but its type `[a0]' has none
In the expression: 0 : 1 : zipWith (+) fibs $ tail fibs
In an equation for `fibs':
fibs = 0 : 1 : zipWith (+) fibs $ tail fibs
fibonacci.hs:1:16:
Couldn't match expected type `[a0]' with actual type `[a1] -> [a1]'
In the return type of a call of `zipWith'
Probable cause: `zipWith' is applied to too few arguments
In the second argument of `(:)', namely `zipWith (+) fibs'
In the second argument of `(:)', namely `1 : zipWith (+) fibs'
And of course, since $ is a function, things like:
fibs = 0 : 1 $ zipWith (+) fibs (tail fibs)
won't work, so at the very least the explanation my professor gave was an oversimplification. While writing this post, I tried to place the parentheses so that the error would be the same. I got:
fibs = (0 : 1 : zipWith (+) fibs) $ tail fibs
and
fibs = (0 : 1 : zipWith (+) fibs) (tail fibs)
which both gave me exactly the same error message (except for the column numbers, of course). Why is this? Is a b $ c d equivalent to (a b) (c d) rather than a b (c d)? I think this all has to do with function precedence and/or associativity, but I don't know the specifics. I don't know how you can see what precedence level a function has (except trying a lot of combinations) and I can't find it with google either.
I hope somebody can help me figure this out!
This is a precedence issue. You have two infix operators here, :
and $
. As an infix operator, :
has higher precedence than $
, so it binds more tightly. You can ask about precedence in ghci
>> :i :
data [] a = ... | a : [a] -- Defined in `GHC.Types`
infixr 5 :
>> :i $
($) :: (a -> b) -> a -> b
infixr 0 $
The infixr
means that the operator groups to the right (so that the expression a + b + c
is interpreted as a + (b + c)
) and the number gives the precedence (higher = more tightly binding).
Also, you need to know that function application has the highest precedence (tighest binding). So in your two expressions, this one
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
is grouped like
fibs = 0 : (1 : (zipWith (+) fibs (tail fibs)))
whereas this
fibs = 0 : 1 : zipWith (+) fibs $ tail fibs
is grouped like
fibs = (0 : (1 : (zipWith (+) fibs))) $ (tail fibs)
which gives you an error, because the expression to the left of $
should be a function, but in your case it is a list.