Search code examples
javascalaclojurejvmjvm-languages

Packages in JVM languages


In Java, classes are placed in a package with a declaration like package com.acme.foo and by putting your source files in a subdirectory like com/acme/foo.

I'm working on a JVM language intended to be more don't repeat yourself in style than Java, so I'm going to use one or other of these mechanisms but not both, and I'm wondering which to use.

How do other JVM languages like Scala and Clojure handle it? Do they require both mechanisms or just one, and if so which one?


Solution

  • As mentioned in the comments to the question Travis Brown is correct, scalac does not place any such constraints or restrictions on the paths or names of files of the source and you can even specify multiple packages in one file if you wish.

    However, the bytecode generated by scalac puts the bytecode classes files in the necessary directory structure required by the appropriate classloader.

    Here are some examples showing the flexibility (I do not necessarily advocate these styles, just showing the flexibility).

    // in packages1.scala file in local directory
    package my {
      package states {
        sealed trait State
        case class CheckingOut(shoppingCartId: Long) extends State
        case class ConfirmedOrder(orderId: Long) extends State
        case class ItemShipped(orderId: Long, itemId: Long, quantity: Int) extends State
      }
    }
    

    And this...

    // in packages2.scala file
    package com.foo.bar
    
    sealed trait Scale
    case object WebScale extends Scale
    case object LolScale extends Scale
    case object RoflScale extends Scale
    case object WatScale extends Scale
    

    And this style is possible too:

    // in packages3.scala file
    package foo {
      package awesomeness {
        class Main extends App {
          println("Bananas are awesome")
        }
      }
    }
    
    package foo {
      package lameness {
        class Main extends App {
          println("Congress is pretty lame, honestly")
        }
      }
    }
    

    As well as this...

    package foo
    package bar
    
    // now we are in package foo.bar for remainder of file unless another package statement is made
    

    Here is the resulting source and compiled bytecode tree:

    $ tree
    .
    ├── com
    │   └── foo
    │       └── bar
    │           ├── LolScale$.class
    │           ├── LolScale.class
    │           ├── RoflScale$.class
    │           ├── RoflScale.class
    │           ├── Scale.class
    │           ├── WatScale$.class
    │           ├── WatScale.class
    │           ├── WebScale$.class
    │           └── WebScale.class
    ├── foo
    │   ├── awesomeness
    │   │   ├── Main$delayedInit$body.class
    │   │   └── Main.class
    │   └── lameness
    │       ├── Main$delayedInit$body.class
    │       └── Main.class
    ├── my
    │   └── states
    │       ├── CheckingOut$.class
    │       ├── CheckingOut.class
    │       └── State.class
    ├── packages1.scala
    ├── packages2.scala
    └── packages3.scala
    
    8 directories, 19 files
    

    I am not sure if Clojure supports such flexibility but Clojure convention is to use the Java convention of structuring source code with it's commonly used build tool lein (see leiningen tutorial here).

    The one thing to note, however, is that in both Scala and Clojure there appears to be a departure from the $DOMAIN.$APPLICATION format that is often used in the Java world (e.g. com.oracle.jdbc..., org.hibernate.session..., etc). In Scala you will see the $DOMAIN part of the package name being taken out completely (e.g. scalaz..., akka.{actor,io, ...}, etc.).

    Also of note is the way you can import from packages in Scala:

    • Import all public "things" in foo.bar package: import foo.bar._
    • Import just one class/trait/etc (just like Java): import foo.bar.Baz
    • Import one class/trait/etc from package and rename it in current scope: import foo.bar.{Baz => FooBarBaz}
    • Import a subset of things from package: import foo.bar.{Baz, Boo, Bla}

    Also of note is package private scoping in Scala:

    package foo.bar
    
    class Baz{
      private[bar] def boo[A](a: A)
      private[foo] def bla[A](a: A)
    }
    

    Above boo is private to the foo.bar package (and subpackages) and bla is private to foo and all it's subpackages.

    For further details read the Scala language specification and related links: