Understand syntactic sugar for lambda expressions

421 views Asked by At

I am struggling to understand the behavior of type inference. For example this fails to compile:

import math._
object Distance {

  def euclidean (p: Seq[Double], c: Seq[Double]) = {
    val d = (p,c)
      .zipped map (_ - _)
      .map pow(_,2.0)
      .foldLeft(0.0)(_+_)
    sqrt(d)
  }
}

with:

Error:(5, 17) missing parameter type for expanded function ((x$3) => scala.Tuple2(p, c).zipped.map(((x$1, x$2) => x$1.$minus(x$2))).map.pow(scala.Tuple2(x$3, 2.0).foldLeft(0.0)(((x$4, x$5) => x$4.$plus(x$5))))) .map pow(_,2.0)

I somehow do not get how the de-sugaring works and I end up having to sprinkle around type declarations and parenthesis or getting rid of infix notations in favor of explicit method calls (with the .).

For example this one works:

import math._
object Distance {

  def euclidean (p: Seq[Double], c: Seq[Double]) = {
    val d = (p,c)
      .zipped.map (_ - _)
      .map ( (x:Double) => pow(x,2.0) )
      .foldLeft(0.0)(_+_)
    sqrt(d)
  }
}

but no chance to have a cute oneliner:

(p,c).zipped map pow(_ - _, 2.0)

I'd be interested in understanding the rules of the game with a for dummies explanation.

1

There are 1 answers

5
Gregor Raýman On

The problem seems to be the infix notation. The rules are actually quite simple: A method taking one parameter can be written in an infix notation. So that you can write a b c instead of a.b(c).

It is however not that simple, because by omitting the explicit dots and parentheses, there must be something else deciding the priority of the operators. So that the compiler can decide that 1+2*3 is 1.+(2.*(3)) and not (1.+(2)).*(3). The precedence of the operators part of the specification you have linked and it is (simply said) governed by the leading symbol of the operator.

Another important detail to note is operators ending with a : bind the parameters from the right. So a :: b is equivalent to b.::(a).

Another tricky thing are the parentheses. In the dot notation they simply wrap the parameter lists. In the operator notation they might need to wrap the parameters themselves (e.g. the function literals).

Btw: Your one liner can be written like this: (p,c).zipped map {(a, b) => pow(a - b, 2.0)} Note that I've wrapped the function literal with {}, that is just for readability, () would work too.