Modules in Scala

I just saw a thread on Lambda the Ultimate where I think the expressive power of Scala in comparison to Standard ML's module system was misrepresented. I don't want to go into all of the issues at the moment, but I figured out would point out that you can get the same structural typing, opaque sealing, and even the equivalent of SML's where type clause.

For example, consider the following SML signature:

  1.  
  2. signature Nat = sig
  3. type t
  4. val z: t
  5. val s: t -> t
  6. end
  7.  

This signature can be translated in to Scala as:

  1.  
  2. type Nat = {
  3. type T
  4. val z: T
  5. def s(arg: T): T
  6. }
  7.  

It is then possible to create an implementation of this type, and opaquely seal it (hiding the definition of T). In SML:

  1.  
  2. structure nat :> Nat = struct
  3. type t = int
  4. val z = 0
  5. fun s n = n + 1
  6. end
  7.  

In Scala:

  1.  
  2. val nat : Nat = new {
  3. type T = Int
  4. val z = 0
  5. def s(arg: Int) = arg + 1
  6. }
  7.  

In many cases when programming with SML modules it is necessary or convenient to give a module that reveals the definition of an abstract type. In the above example, this can be done by adding a where type clause to the first line:

  1.  
  2. structure nat :> Nat where type t = int = struct
  3. ...
  4.  

We can do the same thing in Scala using refinements:

  1.  
  2. val nat : Nat { type T = Int } = new {
  3. ...
  4.  

Great, right? Well, almost. The problem is that structural types are still a bit buggy in Scala compiler at present. So, while the above typechecks, you can't quite use it yet:

  1.  
  2. scala> nat.s(nat.z)
  3. java.lang.NoSuchMethodException: $anon$1.s(java.lang.Object)
  4. at java.lang.Class.getMethod(Class.java:1581)
  5. at .reflMethod$Method1(<console>:7)
  6. at .<init>(<console>:7)
  7. at .<clinit>(<console>)
  8. at RequestResult$.<init>(<console>:3)
  9. at RequestResult$.<clinit>(<console>)
  10. at RequestResult$result(<console>)
  11. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  12. at sun.reflec...
  13.  

There were some issues raised about how faithful an encoding of SML functors, and well-known extensions for higher-order functors, one can get in Scala. Indeed, off the top of my head it is not entirely clear. So I need to think more about that before I write some examples.

7 Comments »

  1. Daniel Spiewak said,

    May 26, 2008 @ 6:56 pm

    Nice!

    BTW, I have a GeSHi definition file for SML if you want it. I wrote it for my blog originally, so I didn’t bother putting colors into the PHP (it’s dependent on CSS and/or a hacky modification of geshi.php), but it wouldn’t be too hard to modify to work with a conventional WP-Syntax setup..

  2. James Iry said,

    May 26, 2008 @ 9:02 pm

    I was stubbornly trying to stick with the “object” keyword as in
    type Nat = {…
    object MyNat extends Nat {…

    or

    object MyNat extends AnyRef with Nat {…

    I don’t know why I hadn’t considered just val MyNat = new Nat {…

    I think it’s because I wanted a top level construct.

  3. ∃xistential Type » Functors in Scala said,

    May 26, 2008 @ 9:42 pm

    […] on my earlier entry on modules in Scala, I’ll give an encoding of Standard ML style functors here. You can get a pretty close approximation […]

  4. washburn said,

    May 26, 2008 @ 9:51 pm

    @Daniel: That would be great. I actually need to do a little more work on the GeSHi definition I wrote for Scala, if for no other reason than to make it use a more coherent color scheme.

    @James: It probably just seemed more natural to me as I am used to thinking about these things in terms of Featherweight Scala which only has traits. It is reasonable to think of objects as modules that have not been ascribed a signature. For example, I could have written my example above like:

    scala> type Nat = { type T; val z: T; def s(arg: T): T }
    defined type alias Nat
    
    scala> object MyNat {
         |   type T = Int
         |   val z = 0
         |   def s(arg: Int) = arg + 1
         | }
    defined module MyNat
    
    scala> val opaqueNat : Nat = MyNat
    opaqueNat: Nat = MyNat$@ec8f6e
    
  5. James Iry said,

    May 26, 2008 @ 10:48 pm

    As I thought about it, I became curious why there’s a problem with “object MyNat extends Nat {…”. It seems pretty clear that I would just be asking the type checker to verify that MyNat is compatible with the Nat type at the point that MyNat is defined. Even with abstract classes the compiler could just quietly create a trait/interface for Nat and use the standard mechanisms for ensuring that instantiable subclasses/objects implement that trait.

    e.g. (this is obviously silly, but…)
    type Nat = {…}

    abstract class MyPartialNat extends Nat {
    type T = int
    val z = 0
    }

    object MyNat extends MyPartialNat {
    def s(arg:Int) = arg + 1
    }

    object MyWeirdNat extends MyPartialNat {
    def s(arg:Int) = arg – 1
    }

    could translate into

    type Nat {…}

    trait Nat$Trait {…} // compiler generated trait

    abstract class MyParitalNat extends AnyRef with Nat$Trait {

  6. washburn said,

    May 26, 2008 @ 11:08 pm

    @James: Indeed, I think that the restriction to class types could be relaxed a little. Certainly it is not problem for Featherweight Scala, but there may be subtle issues trying to compile some of these things for the JVM.

    As another example, it would be nice to be able to “premix” traits that are commonly used together without creating a new trait:

    type AB = A with B
    new AB { } // doesn't work
    

    Instead of having to always expand it out everywhere. Furthermore, it is just strange that with does not associate:

    new A with B with C { } // okay
    new A with (B with C) { } // doesn't work
    new (A with B) with C { } // doesn't work
    new (A with B) { } // doesn't work
    

    I think Martin has said he would at least commit to fixing the associativity issue, but we’ll see whether it can be relaxed further.

  7. ∃xistential Type » Even more modules in Scala said,

    May 26, 2008 @ 11:29 pm

    […] using N.f on N.a.x is even well-typed, but still encounters the same problems with evaluation I mentioned earlier. And yes, the type N.a.T is […]

RSS feed for comments on this post · TrackBack URI

Leave a Comment