0

I'm trying to understand why this two evaluations: (init . cuts) [1,2,3] and init . cuts [1,2,3] are different, where:

cuts :: [a] -> [([a],[a])]
cuts xs = zipWith splitAt [0..length xs] (repeat xs)

The first gives the result that I expected: [([],[1,2,3]),([1],[2,3]),([1,2],[3])], but the second returns this error:

<interactive>:389:8:
    Couldn't match expected type `a0 -> [a1]'
                with actual type `[([a2], [a2])]'
    In the return type of a call of `cuts'
    Probable cause: `cuts' is applied to too many arguments
    In the second argument of `(.)', namely `cuts [1, 2, 3]'
    In the expression: init . cuts [1, 2, 3]

I assume that init . cuts [1,2,3] = init (cuts[1,2,3]), is this correct?

Thanks,
Sebastián.

Fof
  • 407
  • 2
  • 8

1 Answers1

5

This is because of fixity, or order of operations. When the compiler sees

(init . cuts) [1, 2, 3]

It parses this as the function inits . cuts with the argument [1, 2, 3]. When it sees

init . cuts [1, 2, 3]

It parses this as the function inits composed with the function cuts [1, 2, 3], but since cuts [1, 2, 3] is not a function, it raises a type error. This occurs because function application always takes higher precedence over operator application. That's why you can write expressions like

f = zipWith (+) [1..] . filter (< 10) . take 5

instead of

f = (zipWith (+) [1..]) . (filter (< 10)) . (take 5)

If operator precedence could be higher than function application precedence, then you might have to use the latter form depending on the operator precedence, and then expressions become much harder for the compiler and for programmers to parse. It could mean that there might be ambiguity with something like

x 1 <#> y

Since it could be parsed as

(x 1) <#> y

Or

x (1 <#> y)

Depending on precedence (I just made up an operator <#>).


As @chepner rightly points out, if you want to avoid parentheses (as many Haskellers do), you can instead use the $ operator, which has a carefully chosen fixity to allow you to do

init . cuts $ [1, 2, 3]

The rule of thumb with $ operator is that it is the same has the same effect1 as taking whatever appears to the right and wrapping it in parentheses, so a complex expression like

func (func (func (func (func 1))))

Can instead be written in the much nicer form

func $ func $ func $ func $ func 1

Or even

func . func . func . func . func $ 1

1 As @Cubic has noted, some may think that $ is some special syntax of Haskell. This is not the case. It is defined very simply as

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

In fact, this function is identical to the id function, just expressed as an infix operator with very low precedence associating to the right, and that fixity is what makes the operator useful for expressing code very cleanly. It can be equivalent to using more parentheses, but that does not make it an alternate syntax to them. It's just another function.

bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • 1
    Probably worth noting there is a low-precedence function application operator for situations like this: `init . cuts $ [1,2,3]` which is equivalent to `(init . cuts) [1, 2, 3]`. – chepner May 06 '14 at 17:17
  • 1
    @chepner Edited to include some examples. – bheklilr May 06 '14 at 17:22
  • 1
    Also, see [this answer](http://stackoverflow.com/questions/22643991/infix-operator-precedence-in-haskell/22644025#22644025) I wrote up a while ago on the same problem. – bheklilr May 06 '14 at 17:23
  • @bheklilr Ok, as you said "The rule of thumb with $ is that it is the same as taking whatever appears to the right and wrapping it in parentheses", but why here is different init . cuts $ [1,2,3] = (init . cuts) [1, 2, 3]? – Fof May 06 '14 at 17:33
  • 1
    @Seba Maybe I should have said it as "takes whatever appears to the right, wraps it in parentheses, and passes it to whatever is to the left wrapped in parentheses _up to the next `$`_". So something like `f $ g . h $ 1` gets turned into `(f) ((g . h) (1)) === f ((g . h) 1)`. – bheklilr May 06 '14 at 17:43
  • 3
    @bheklilr I still think that's a terrible way to introduce `$`. I've seen more than one person ending up thinking that `$` is some special syntax for literally wrapping parts of expressions in parantheses. – Cubic May 06 '14 at 20:29
  • @Cubic I could see where the confusion arises. I did make sure to use the term _operator_ when describing it, but I'll edit my answer to make it more clear that `$` is just a normal function with useful fixity. – bheklilr May 06 '14 at 21:04