Michael Lafayette
Michael Lafayette

Reputation: 3072

Scala infix notation

Here is my code...

val strings: Enumerator[String] = Enumerator("1","2","3","4")

//create am Enumeratee using the map method on Enumeratee

val toInt: Enumeratee[String,Int] = Enumeratee.map[String]{ 
    (s: String) => s.toInt 
}
val toSelf: Enumeratee[String,String] = Enumeratee.map[String]{ 
    (s: String) => s 
}
List("Mary", "Paul") map (_.toUpperCase) filter (_.length > 5)

val r1 = strings |>> (toSelf &>> (toInt &>> sum))
val r2 = strings |>> toSelf &>> (toInt &>> sum)
val r3 = strings |>> toSelf &>> toInt &>> sum // does not compile

val foo1 = strings &> toInt |>> sum
val foo2 = strings &> (toInt |>> sum) // does not compile
val foo3 = (strings &> toInt) |>> sum

The symbols |>>, &>>. &> are methods. I am confused about the way the compiler is putting parenthesis around them. In the line:

List("Mary", "Paul") map (_.toUpperCase) filter (_.length > 5)

The compiler is inserting the parenthesis like this:

((List("Mary", "Paul") map (_.toUpperCase))) filter (_.length > 5)

In actuality it compiles to:

List("Mary", "Paul").map(((x$3: String) => x$3.toUpperCase()))(List.canBuildFrom[String]).filter(((x$4: String) => x$4.length().>(5)))

In the subsequent example:

strings |>> toSelf &>> (toInt &>> sum)

The compiler is inserting the parenthesis like this:

strings |>> (toSelf &>> (toInt &>> sum))

In actuality it compiles to:

strings.|>> (toSelf.&>> (toInt.&>>(sum)))

Sometimes it seems like the compiler is inserting parenthesis from right to left (second example) and other times it seems the compiler is inserting parenthesis left to right (first example). Sometimes, like in

val r3 = strings |>> toSelf &>> toInt &>> sum

I expect it to insert parenthesis like

val r3 = strings |>> (toSelf &>> (toInt &>> sum))

and instead I get a compiler error.

Can somebody please explain the rules to parenthesis insertion for whitespace delimited methods?

Upvotes: 2

Views: 830

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149598

Operation in infix notation have a precedence defined to them, as mentioned in the specification:

The precedence of an infix operator is determined by the operator's first character. Characters are listed below in increasing order of precedence, with characters on the same line having the same precedence:

(all letters)
|
^
&
= !
< >
:
+ -
* / %
(all other special characters)

Precedence and associativity of operators determine the grouping of parts of an expression as follows.

  • If there are several infix operations in an expression, then operators with higher precedence bind more closely than operators with lower precedence.

  • If there are consecutive infix operations e0; op1; e1; op2… opn; en with operators op1,…,opnop1,…,opn of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as (…(e0;op1;e1);op2…);opn;en. Otherwise, if all operators are right-associative, the sequence is interpreted as e0;op1;(e1;op2;(…opn;en)…).

  • Postfix operators always have lower precedence than infix operators. E.g. e1; op1; e2; op2 is always equivalent to (e1;op1;e2);op2

According to the spec, your second expression should be:

strings.|>>((toSelf.&>>toInt).&>>(sum)))

Since | has a lesser precedence than &, it is invoked last, and then &>> are left associative so they're invoked from left to right.

Upvotes: 2

Related Questions