Reputation: 918
What precisely is the difference between .
and when used to invoke functions from objects in Scala?
For some reason, I get variations, like:
scala> val l:List[Int] = 1::Nil
l: List[Int] = List(1, 2, 3)
scala> l foldLeft(0)((hd, nxt) => hd + nxt)
<console>:13: error: Int(1) does not take parameters
| foldLeft(1)((hd, nxt) => hd + nxt)
^
scala>l.foldLeft(0)((hd, nxt) => hd + nxt)
res2: Int = 2
(And while I'm at it, what's the name of that operation? I kept trying to find the strict definition of the .
operator and I have no idea what it's called.)
Upvotes: 4
Views: 2467
Reputation: 3072
Desugar it with "-Xprint:parser"
or "-Xprint:typer"
Example 1 Desugared:
scala> (List(1,2) foldLeft 0)((hd, nxt) => hd + nxt)
...
List(1, 2).foldLeft(0)(((hd, nxt) => hd.$plus(nxt)))
...
immutable.this.List.apply[Int](1, 2).foldLeft[Int](0)(((hd: Int, nxt: Int) => hd.+(nxt)));
As you can see, (List(1,2) foldLeft 0)
translates into (List(1, 2).foldLeft(0))
in the parser phase. This expression returns a curried function that takes in the second set of parenthesis to produce a result (remember that a curried function is just a function that takes in an argument and returns another function with one fewer argument).
Example 2 Desugared:
scala> List(1,2) foldLeft(0)((hd, nxt) => hd + nxt)
...
List(1, 2)(foldLeft(0)(((hd, nxt) => hd.$plus(nxt))))
...
<console>:8: error: not found: value foldLeft
List(1,2) (foldLeft(0)((hd, nxt) => hd + nxt))
The parenthesis are going around (foldLeft(0)((hd, nxt) => hd + nxt))
.
Style:
The way you are supposed to use space delimited methods is 1 object followed by 1 method followed by 1 set of parenthesis, which produces a new object that can be followed by a new method.
obj method paramerer // good
obj method1 paramerer1 method2 paramerer2 // good
obj method1 paramerer1 method2 paramerer2 method3 paramerer3 // compiles, but might need to be broken up
You can follow an object with postfix a method that takes no parameters, but this isn't always the approved style, especially for accessors.
foo.length // good
foo length // compiles, but can be confusing.
Space delimited methods are normally reserved for either pure functions (like map, flatmap, filter) or for domain specific languages (DSL).
In the case of foo.length
, there is no ()
on length
, so the whitespace isn't necessary to convey the idea that length is pure.
Upvotes: 0
Reputation: 1678
Having space instead of dot is called postfix notation if there are no arguments in the called function on the object, or infix notation if there is an argument that the function requires.
Postix example: l sum
, equivalent to l.sum
Infix example: l map (_ * 2)
, equivalent to l.map(_ * 2)
The issue with these notations is that they are inherently more ambiguous in their interpretation. A classic example from math:
Therefore it is not a different operator, but the same as the dot, just susceptible to ambiguity that can lead to syntactical errors like your case or even worse logical errors when you chain infix operators.
You can actually express foldLeft with infix notation in this way:
(l foldLeft 0)((hd, nxt) => hd + nxt)
or even
(0 /: l)((hd, nxt) => hd + nxt)
Where /:
is just an alias for foldLeft and makes use of the unique semantics of operator ending in colon(:), which are interpreted as l./:(0)
(the reverse of the usual).
Upvotes: 7