Scala: Functions and Partial functions are not functions. They are instances of some anonymous clss!

2 minute read

In Scala, you can define a function that takes and returns an Int like so:

val succ = (x: Int) => x + 1

succ is a first class citizen here. It could be passed to other functions and returned from them just like any other value/variable.

But it’s hides its nature. It’s real self is somewhat sinister looking.

val succ = new Function1[Int, Int] {
  def apply(x: Int): Int = x + 1
}

Can you believe this shit?

Now look at how you can define a partial function in scala.

val divide: PartialFunction[Int, Int] = {
    case d: Int if d != 0 => 42 / d
}

Not quite endearing. But ignoring the type definition, we can see that the function only handles inputs that are not zero. Elegant.

Not quite. Look at it’s real nature.

val divide = new PartialFunction[Int, Int] {
  def apply(x: Int) = 42 / x

  def isDefinedAt(x: Int): Boolean = x != 0
}

The Question

Looking at the actual form of both a normal function and a regular function, we notice something weird.

We instantiate an object using the new keyword. In Scala, we can only instantiate a class. But here both Function1 and PartialFunction are traits! So how does this syntax work?

It works because when the new is followed by a block with methods, values, variables defined in it, then the block of code is treated as an anonymous class. We can confirm this by calling getClass() on the created object.

Here’s a full example:

val anonObject = new {
  def hello: String = "hi"
}

anonObject.hello
// "hi"

anonObject.getClass
// Class[_ <: AnyRef] = class $anon$1

What about traits abstract classes that a regular class can extend/mixin into itself?

Here’s were the syntax gets weird and I got so confused.

If you have a trait that you want to mix into the anonymous class, then you can’t just do this:


trait God {
  def answer: Int
}

val anonObject = new {
  def hello: String = "hi"

  override def answer: Int = 42
} with God

Isn’t this what we’d think intuitively? But this is a syntax error! Instead we have to define it like this:

trait Anger {
  def getAngry: String
}

val dog = new Anger { // NOTE the trait's placement!
  def hello: String = "woof"

  override def getAngry: String = "BARK!!"
}

dog.hello // 'woof'
dog.getAngry // 'BARK!!'

We have to put the trait name next to new. It looks confusing as hell.

But at least I figured it out now.

Tags:

Updated: