Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A class imported from a companion not usable as the constructor parameter default value

Consider following code:

object Main extends App {
  object Project {
    case class Config(rules: Seq[String] = Seq.empty)
  }

  import Project._

  //case class Project(root: String, config: Config) // compiles fine

  //case class Project(root: String, config: Project.Config = Project.Config()) // compiles fine

  case class Project(root: String, config: Config = Config()) // error: not found: type Config

}

Why does the last version not compile (same with Config = Config.apply())?

like image 236
Suma Avatar asked Apr 08 '17 11:04

Suma


1 Answers

It is not clear to me if this is a bug or not, but here is why it produces an error:

Consider this, which works:

import Project._

object Project {
  case class Config()
}

case class Project(config: Config = Config())

When you add a default argument the compiler generates a method to calculate the value. When that value is a constructor default, that method is added to the companion object of the class. So the compiler will generate this method:

def <init>$default$1: Project.Config = Config() 

Which will get added to your Project object.

The Scala type checker generates an object tree of Contexts. Each context has a reference to the context of it's outer scope. So the generated method gets a context and that generated method's outer scope is the Project companion object.

When the type checker attempts to resolve Config() it traverses all the enclosing contexts and cannot find Config (I am not sure why, and this may be a bug).

Once it has exhausted the contexts it resolves the imports which has the import Project._! The type checker is happy because it can now traverse the imports and find the apply method.

Now when you move the import below Project:

object Project {
  case class Config()
}

import Project._

case class Project(config: Config = Config())

In this case the imports available to the generated method does not have the Project._ import (this may also be a bug), I'm assuming because it's below the object definition which is where the generated method lives. The type checker then throws an error because it can't find Config.

What appears to be happening is when the type checker is resolving Config() it needs the import above the Project companion object as it needs to process the import to be able to resolve it and unless the import is above Project that import is not in scope.

For those who wish to debug further take a look at Contexts.lookupSymbol which is where the lookup is happening

like image 84
Matt Fowler Avatar answered Nov 03 '22 21:11

Matt Fowler