Skip to content

Instantly share code, notes, and snippets.

@megri
Created March 9, 2018 23:09
Show Gist options
  • Save megri/22bf37fb6406ff104de18c58ff0a6404 to your computer and use it in GitHub Desktop.
Save megri/22bf37fb6406ff104de18c58ff0a6404 to your computer and use it in GitHub Desktop.

Reworked proposal for unification of package, object and package object

Keyword changes

  • object becomes module
  • package becomes module
  • package object becomes module
  • Support for top-level definitions

Syntax changes

package com.foo

becomes

module com.foo

package com.foo

object bar {
  def x = 1
}

becomes

module com.foo

module bar {
  def x = 1
}

which is the same as

module com.foo.bar

def x = 1

package com.foo

package object bar {
  val x = 42
  object Y {
    def z = "hello"
  }
}

becomes

module com.foo.bar

val x = 42

module Y {
  def z = "hello"
}

Implicits

Symbols defined implicit are not allowed as top-level definitions. This is a logical restriction to make implicit symbols easier to locate in source-code. If a module is to define implicit symbols they need to be explicitly wrapped in a sealed module definition:

module com.foo

[sealed] module bar extends Implicits with MoreImplicits {
  val x = 42

  module Y {
    def z = "hello"
  }
}

This corresponds to the current solution:

package com.foo

package object bar extends Implicits with MoreImplicits{
  val x = 42
  object Y {
    def z = "hello"
  }
}

Concepts

Module paths

The path of a module corresponds to a standard Java-path.

Modules are first-class

Modules are, from a user-perspective, first-class: every part of a module path can be treated as an instance and passed around.

Module self-type

Where com.foo.bar is a module (as in, not a class), com.foo.bar.type is the type of the module.

Nominal modules

Top-level definitions

Sealed modules

Sealed modules are unique on the class-path: Classes, traits and other module paths can be defined from the path of a sealed module, but not top-level definitions.

Only sealed modules can extend traits or classes. The sealed-keyword is optional for modules that inherit from other types.

These are allowed:

File1:
module foo
sealed module bar {}

File2:
module foo.bar
class Baz

File3:
module foo.bar.baz
def main( args: Array[String] ): Unit =
  println( "Hello, World!" )

These are not allowed and result in a compilation error:

File1:
module foo
sealed module bar {}

File2:
module foo.bar
val number = 42 // Error: can't add top-level definitions to a sealed module

Compilation

Source structure are compiled mostly like they are today, with a few caveats.

module com.foo

trait Bar
class Baz extends Bar

yields the corresponding pseudo-definitions in bytecode

package com.foo
interface Bar
public class Baz implements Bar{}

Sealed modules compile to a module-path and a singleton:

module foo

sealed module bar {
  class Baz
}

becomes

package foo.bar
public class bar$MODULE {
  public static final bar$MODULE instance = …
  public class Baz {}
}

Top-level definition unification

Top-level definitions are unified in a deferred compilation stage to allow additional definitions after an application has been packaged. This corresponds to adding new types under an existing (third party) package.

Nominal collision rules apply: it is not possible to declare two methods foo under the same module path but in different files. Overloading of top-level definitions is allowed in the context of one compilation unit but disallowed anywhere else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment