Question

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?

Was it helpful?

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:

OTHER TIPS

Clojure is sort of like Java, but there aren't really classes so it's a bit different.

If there's a namespace project.foo, then the Clojure compiler will expect to find it in the project directory under a source root in a file called foo.clj.

e.g. Assume src is the directory where source files are kept. Then the project.foo namespace would be in src/project/foo.clj with the a declaration like so (ns project.foo) at the top.

I think it's difficult to get away from this really, because at runtime, when Clojure goes to load the file (most Clojure is bundled as source), the Clojure runtime will load the file as a resource, which requires it to be in the right place in the directory hierarchy (whether jar or file based).

Personally, I don't really mind the package name == directory location convention either. It makes it easy for tools to find files either at compile or runtime, and if I'm just using emacs, makes it easy for me to find them as well. It also feels a bit more organized then just a bunch of files in a directory, although that's probably more a matter of what I'm used to after working with it all this time.

I dont have any but a small experience with Clojure but I do have some experience with Scala, and scala uses subdirectories as their packages as well but a nifty feature in it are the ability rename the class similar to the C++ typedef keyword functionality. An interesting idea would be if you created a linker similar to C++'s and allowed a typedef functionality but still keeping it in the JVM.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top