Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Actual precedence for infix operators in Scala

The May 24, 2011 Scala Language Specification has a typo in section 6.12.3 as discovered here. This was acknowledged on the mailing list.

What is the actual precedence for the infix operators?

like image 332
huynhjl Avatar asked Oct 01 '11 07:10

huynhjl


People also ask

Which operator has the highest order to precedence?

The logical-AND operator ( && ) has higher precedence than the logical-OR operator ( || ), so q && r is grouped as an operand. Since the logical operators guarantee evaluation of operands from left to right, q && r is evaluated before s-- .

What is infix notation in Scala?

For a long time, Scala has supported a useful “trick” called infix operator notation. If a method takes a single argument, you can call it without the period after the instance and without parentheses around the argument.

Which operator precedence is correct by default and/or not?

The precedence and meaning of some operators depends on the SQL mode: By default, || is a logical OR operator. With PIPES_AS_CONCAT enabled, || is string concatenation, with a precedence between ^ and the unary operators. By default, ! has a higher precedence than NOT .

How do operators work in Scala?

In Scala, operators are methods. Any method with a single parameter can be used as an infix operator. For example, + can be called with dot-notation: Scala 2 and 3.


1 Answers

I thought it would be fun figuring it out by testing it, I wrote the following code to be executed in the REPL. Given:

val ops = List(
  "letter", "|", "^", 
  "&", "<", ">", "!", 
  "+", "-", "*", "/", "%", "?", 
  "=?", // add ? to prevent assignment
  ":?"  // add ? to prevent right-association
)

First generate an intermediate scala file that use and test the operators.

import java.io._

// generate a class with all ops operators defined
// where operator combines in a way we can figure out the precedence
val methods = ops.map("""def %s(o: Op) = Op("["+o.v+v+"]")""".format(_))
val body = methods.mkString("\n")
val out = new PrintWriter(new FileWriter("Op.scala"))
out.println("case class Op(v: String) {\n%s\n}".format(body))

// generate tests for all combinations and store in comps
// Op(".") op1 Op(".") op2 Op(".") v returns "[[..].]" when op2 > op1
// returns "[.[..]]" when op1 <= op2
def test(op1: String, op2:String) = {
  """("%s","%s") -> (Op(".") %s Op(".") %s Op(".")).v.startsWith("[[")""".
    format(op1, op2, op1, op2)
}
val tests = for (op1 <- ops; op2 <- ops) yield { test(op1, op2) }
out.println("val comps = Map[(String, String), Boolean](%s)".format(
  tests.mkString(",\n")))
out.close

Then Load Op class, run tests and load comps

:load Op.scala

// order operators based on tests
val order = ops.sortWith((x,y) => comps(x -> y))

// if op1 or op2 don't have higher precedence, they have the same precedence
def samePrecedence(op1: String, op2: String) = 
  !comps(op1 -> op2) && !comps(op2 -> op1)

def printPrecedenceGroups(list: List[String]): Unit = {
  if (list != Nil) {
    val (same, rest) = list.span(op => samePrecedence(op, list.head))
    println(same.mkString(" "))
    printPrecedenceGroups(rest)
  }
}

printPrecedenceGroups(order)

This prints:

letter
|
^
&
! =?
< >
:?
+ -
* / %
?

So the main difference with the spec is < > needs to be switched with = !.

like image 170
huynhjl Avatar answered Sep 23 '22 23:09

huynhjl