When programming in Scala, I do more and more functional stuff. However, when using infix notation it is hard to tell when you need parenthesis and when you don\'t.
The spec doesn't make it clear, but my experience and experimentation has shown that the Scala compiler will always try to treat method calls using infix notation as infix operators. Even though your use of mkString is postfix, the compiler tries to interpret it as infix and so is trying to interpret "map" as its argument. All uses of postfix operators must either be immediately followed by an expression terminator or be used with "dot" notation for the compiler to see it as such.
You can get a hint of this (although it's not spelled out) in A Tour of Scala: Operators.
This is what I put together for myself after reading the spec:
a.m(b)
can be written a m b
.a.m
can be written a m
.For instance a.##(b)
can be written a ## b
and a.!
can be written a!
foo bar baz
means foo.bar(baz)
while foo bar baz bam
means (foo.bar(baz)).bam
and foo bar baz bam bim
means (foo.bar(baz)).bam(bim)
. a.m.m
is valid but a m m
is not as it would parse as exp1 op exp2
.Because there is a version of mkString
that takes a single parameter it will be seen as an infix opertor in fromFile(file) mkString map caesar(k)_
. There is also a version of mkString
that takes no parameter which can be used a as postfix operator:
scala> List(1,2) mkString
res1: String = 12
scala> List(1,2) mkString "a"
res2: String = 1a2
Sometime by adding dot in the right location, you can get the precedence you need, e.g. fromFile(file).mkString map { }
And all that precedence thing happens before typing and other phases, so even though list mkString map function
makes no sense as list.mkString(map).function
, this is how it will be parsed.
Here's a simple rule: never used postfix operators. If you do, put the whole expression ending with the postfix operator inside parenthesis.
In fact, starting with Scala 2.10.0, doing that will generate a warning by default.
For good measure, you might want to move the postfix operator out, and use dot notation for it. For example:
(fromFile(file)).mkString map caesar(k)_
Or, even more simply,
fromFile(file).mkString map caesar(k)_
On the other hand, pay attention to the methods where you can supply an empty parenthesis to turn them into infix:
fromFile(file) mkString () map caesar(k)_
The Scala reference mentions (6.12.3: Prefix, Infix, and Postfix Operations)
In a sequence of consecutive type infix operations
t0 op1 t1 op2 . . .opn tn
, all operatorsop1, . . . , opn
must have the same associativity.
If they are all left-associative, the sequence is interpreted as(. . . (t0 op1 t1) op2 . . .) opn tn
.
In your case, 'map
' isn't a term for the operator 'mkstring
', so you need grouping (with the parenthesis around 'fromFile(file) mkString
')
Actually, Matt R comments:
It's not really an associativity issue, more that "Postfix operators always have lower precedence than infix operators. E.g.
e1 op1 e2 op2
is always equivalent to(e1 op1 e2) op2
". (Also from 6.12.3)
huynhjl's answer (upvoted) gives more details, and Mark Bush's answer (also upvoted) point to "A Tour of Scala: Operators" to illustrate that "Any method which takes a single parameter can be used as an infix operator".