Search code examples
scalaclosureslanguage-designjava-8

Closures in Scala vs Closures in Java


Some time ago Oracle decided that adding Closures to Java 8 would be an good idea. I wonder how design problems are solved there in comparison to Scala, which had closures since day one.

Citing the Open Issues from javac.info:

  1. Can Method Handles be used for Function Types? It isn't obvious how to make that work. One problem is that Method Handles reify type parameters, but in a way that interferes with function subtyping.

  2. Can we get rid of the explicit declaration of "throws" type parameters? The idea would be to use disjuntive type inference whenever the declared bound is a checked exception type. This is not strictly backward compatible, but it's unlikely to break real existing code. We probably can't get rid of "throws" in the type argument, however, due to syntactic ambiguity.

  3. Disallow @Shared on old-style loop index variables

  4. Handle interfaces like Comparator that define more than one method, all but one of which will be implemented by a method inherited from Object. The definition of "interface with a single method" should count only methods that would not be implemented by a method in Object and should count multiple methods as one if implementing one of them would implement them all. Mainly, this requires a more precise specification of what it means for an interface to have only a single abstract method.

  5. Specify mapping from function types to interfaces: names, parameters, etc. We should fully specify the mapping from function types to system-generated interfaces precisely.

  6. Type inference. The rules for type inference need to be augmented to accomodate the inference of exception type parameters. Similarly, the subtype relationships used by the closure conversion should be reflected as well.

  7. Elided exception type parameters to help retrofit exception transparency. Perhaps make elided exception type parameters mean the bound. This enables retrofitting existing generic interfaces that don't have a type parameter for the exception, such as java.util.concurrent.Callable, by adding a new generic exception parameter.

  8. How are class literals for function types formed? Is it #void().class ? If so, how does it work if object types are erased? Is it #?(?).class ?

  9. The system class loader should dynamically generate function type interfaces. The interfaces corresponding to function types should be generated on demand by the bootstrap class loader, so they can be shared among all user code. For the prototype, we may have javac generate these interfaces so prototype-generated code can run on stock (JDK5-6) VMs.

  10. Must the evaluation of a lambda expression produce a fresh object each time? Hopefully not. If a lambda captures no variables from an enclosing scope, for example, it can be allocated statically. Similarly, in other situations a lambda could be moved out of an inner loop if it doesn't capture any variables declared inside the loop. It would therefore be best if the specification promises nothing about the reference identity of the result of a lambda expression, so such optimizations can be done by the compiler.

As far as I understand 2., 6. and 7. aren't a problem in Scala, because Scala doesn't use Checked Exceptions as some sort of "Shadow type-system" like Java.

What about the rest?


Solution

  • 1) Can Method Handles be used for Function Types?

    Scala targets JDK 5 and 6 which don't have method handles, so it hasn't tried to deal with that issue yet.

    2) Can we get rid of the explicit declaration of "throws" type parameters?

    Scala doesn't have checked exceptions.

    3) Disallow @Shared on old-style loop index variables.

    Scala doesn't have loop index variables. Still, the same idea can be expressed with a certain kind of while loop . Scala's semantics are pretty standard here. Symbols bindings are captured and if the symbol happens to map to a mutable reference cell then on your own head be it.

    4) Handle interfaces like Comparator that define more than one method all but one of which come from Object

    Scala users tend to use functions (or implicit functions) to coerce functions of the right type to an interface. e.g.

    [implicit] def toComparator[A](f : (A, A) => Int) = new Comparator[A] { 
        def compare(x : A, y : A) = f(x, y) 
    }
    

    5) Specify mapping from function types to interfaces:

    Scala's standard library includes FuncitonN traits for 0 <= N <= 22 and the spec says that function literals create instances of those traits

    6) Type inference. The rules for type inference need to be augmented to accomodate the inference of exception type parameters.

    Since Scala doesn't have checked exceptions it can punt on this whole issue

    7) Elided exception type parameters to help retrofit exception transparency.

    Same deal, no checked exceptions.

    8) How are class literals for function types formed? Is it #void().class ? If so, how does it work if object types are erased? Is it #?(?).class ?

    classOf[A => B] //or, equivalently, 
    classOf[Function1[A,B]]
    

    Type erasure is type erasure. The above literals produce scala.lang.Function1 regardless of the choice for A and B. If you prefer, you can write

    classOf[ _ => _ ] // or
    classOf[Function1[ _,_ ]]
    

    9) The system class loader should dynamically generate function type interfaces.

    Scala arbitrarily limits the number of arguments to be at most 22 so that it doesn't have to generate the FunctionN classes dynamically.

    10) Must the evaluation of a lambda expression produce a fresh object each time?

    The Scala specification does not say that it must. But as of 2.8.1 the the compiler does not optimizes the case where a lambda does not capture anything from its environment. I haven't tested with 2.9.0 yet.