1

I came across a code similar to this, and it was surprised that it even compiles:

scala> val halfSize: PartialFunction[String, Int] = _.length match {
    case even if even % 2 == 0 => even / 2 
}
halfSize: PartialFunction[String,Int] = <function1>

scala> List("x", "xx").collect(halfSize)
res1: List[Int] = List(1)

As far as I known, the valid syntax to define a PartialFunction is a case function:

val halfSize: PartialFunction[String, Int] = { 
     case s if s.length % 2 == 0 => s.length / 2 
}

The first code seems more optimized since it calls length only once. But even in the SLS I was not able to find the explanation of the syntax. Is this an undocumented feature of scalac ?

thibr
  • 607
  • 6
  • 13

2 Answers2

0
val halfSize: PartialFunction[String, Int] = _.length match {
    case even if even % 2 == 0 => even / 2 
}

The underscore (_) in the above function is just a short hand notation that refers to the single argument of the function. Thus the above snippet is just a short form of

val halfSize: PartialFunction[String, Int] = { (x: String) => x.length match {
    case even if even % 2 == 0 => even / 2 
 }
}
rogue-one
  • 11,259
  • 7
  • 53
  • 75
  • but even in the expanded version, it should give a `Function`, not a `PartialFunction` – thibr Aug 12 '17 at 19:20
  • thats because PartialFunction[A,B] extends Function[A,B] as shown here http://www.scala-lang.org/api/2.12.1/scala/PartialFunction.html – rogue-one Aug 12 '17 at 19:22
  • Function is higher is the inheritance, so it can't be assigned to a PartialFunction – thibr Aug 12 '17 at 19:26
  • take a look at this.. https://stackoverflow.com/questions/930698/why-is-partialfunction-function-in-scala – rogue-one Aug 12 '17 at 19:30
  • Quoting the answer: A PartialFunction is just a Function that promises to tell you where it's not defined. In my case, how the compiler infers the `isDefined` method is black magic to me (or at least undocumented) – thibr Aug 12 '17 at 19:35
  • 1
    @pedrofurla This 1) would work incorrectly for functions which have side effects; 2) would be slow if function does a lot of work to calculate the result just to throw it away. – Alexey Romanov Aug 13 '17 at 05:15
  • @AlexeyRomanov I guess you are right, it used to be like that a long long time ago. – pedrofurla Aug 14 '17 at 19:22
0

The rules are given in https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#anonymous-functions:

The eventual run-time value of an anonymous function is determined by the expected type:

  • ...

  • PartialFunction[T, U], if the function literal is of the shape x => x match { … }

In this case the literal does have such a shape, as rogue-one's answer explains, so PartialFunction is allowed as the expected type.

(EDIT: actually, it doesn't, since it matches x.length instead of x. This looks like a minor bug, but one which should be fixed by changing the specification.)

A PartialFunction's value receives an additional isDefinedAt member, which is derived from the pattern match in the function literal, with each case's body being replaced by true, and an added default (if none was given) that evaluates to false.

So in this case it ends up with

def isDefinedAt(x: String) = x.length match {
  case even if even % 2 == 0 => true
  case _ => false
}
Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thanks for the pointer to the relevant part of the spec. What actually surprised is exactly what you added in your EDIT (in the original code, `x.length` was a more elaborated statement). – thibr Aug 13 '17 at 10:34
  • And so we agree this is an undocumented feature ;) – thibr Aug 13 '17 at 10:36