Question

While trying to write a function for transposing a list of lists, I saw something very curious. I tried:

> let abc xs | null (head xs) = [] | otherwise = map head xs : abc $ map tail xs

and got an error. Then I tried:

> let abc xs | null (head xs) = [] | otherwise = map head xs : abc ( map tail xs )
> abc [[1,2,3], [4,5,6], [7,8,9]]
[[1,4,7],[2,5,8],[3,6,9]]

I was led to believe that the $ operator can be used instead of the brackets, and that that's more Haskellish. Why am I getting an error?

Was it helpful?

Solution

An operator is a function that can be applied in a infix position. So $ is a function.

In Haskell, you can define your own functions that can be used in infix position - between the arguments. Then you can also define function application precedence and associativity using infix, infixr, infixl - that is, the clues telling the compiler whether to treat a $ b $ c as (a $ b) $ c, or a $ (b $ c).

The precedence of $ is such that your first expression is interpreted like (map head xs : abc) $ ...

For example, to declare $ as infix, place its name in ():

($) :: (a->b) -> a -> b
f $ x = f x

or composition:

(.) :: (b->c)->(a->b)->a->c
(f . g) x = f $ g x

Arithemtic "operators" are also defined as infix functions in class Num.

Additionally, you can use other functions as infix, by quoting them in backticks `` at the application site. Sometimes it makes the expression look prettier:

f `map` xs == map f xs

(not that in this particular case it makes it look pretty, just to show a simple example)

OTHER TIPS

Adding to Sassa's correct answer, let's dissect the code snippet you provided a bit further:

map head xs : abc $ map tail xs

Two operators are used here: (:) and ($). As noted above, these are interpreted as infix by default because their names consist only of symbols.

Each operator has a precedence which decides how 'tightly' it binds or, perhaps more usefully, which operator is applied first. Your code could be interpreted either as

((map head xs) : abc) $ (map tail xs)

where (:) binds more tightly (is applied before) ($) or as

(map head xs) : (abc $ (map tail xs))

where ($) binds more tightly. Note that I have put parentheses around function application (for example map applied to tail and xs) as well. Function application binds more tightly than any operator and is thus always applied first.

To decide which of the two ways to interpret the code is correct, the compiler needs to get information about which operator should bind more tightly. This is done using a fixity declaration like

infix 8

or in general

infix i

where i is between 0 and 9. Higher values of i mean that an operator binds more tightly. (infixr and infixl may be used to additionally define associativity as explained in Sassa's answer, but this doesn't affect your specific problem.)

As it turns out, the fixity declaration for the ($) operator is

infixr 0 $

as seen in the Prelude documentation. (:) is a bit more 'magic' because it is hard-coded into the Haskell syntax, but the Haskell Report specifies that it has precedence 5.

Now we finally have enough information to conclude that the first interpretation of your code is indeed correct: the precedence of (:), 5, is higher than the precedence of ($), 0. As a general rule of thumb, ($) often doesn't interact well with other operators.

By the way, if an expression contains two different operators which have the same precedence (such as (==) and (/=)), the order in which they should be applied is unclear, so you have to use parentheses to specify it explicitly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top