This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Inherited constructors and dependent types

5 replies
andrevandelft
Joined: 2010-02-07,
User offline. Last seen 34 weeks 1 day ago.

After a few weeks of Scala programming, I was a bit surprised and
disappointed not to find inherited constructors. I could really use
these, to make "fellow" instances of objects. See
http://www.cs.cornell.edu/nystrom/papers/compose-oopsla.pdf, pages 5
and 9.

In principle, inheritable constructors are not hard to define and
implement. However, invoking an inherited constructor on an object p
should yield a dependent type, which is not supported in Scala. The
singleton type "p.type" would almost be appropriate, but it is a bit
too restricted.

For the definition and implementation of the filter methods in
collection classes, inherited constructors and dependent types would
be useful. What is the reason for not having these in Scala? Is there
a chance that they are included as yet?

milessabin
Joined: 2008-08-11,
User offline. Last seen 33 weeks 3 days ago.
Re: Inherited constructors and dependent types

2011/6/18 André van Delft :
> For the definition and implementation of the filter methods in
> collection classes, inherited constructors and dependent types would
> be useful. What is the reason for not having these in Scala? Is there
> a chance that they are included as yet?

Family polymorphism can be encoded fairly straightfowardly in Scala
using path-dependent types, and virtual classes can be too (the
encoding is similar, but a little more cumbersome).

Can you give some specific examples of things you want to be able to do?

Cheers,

Miles

Lars Hupel
Joined: 2010-06-23,
User offline. Last seen 44 weeks 3 days ago.
Re: Inherited constructors and dependent types

Another feasible solution for this would be to use the type class
pattern. (In Java terms, known as the Factory pattern.)

In Scala, this is really easy to accomplish by having a trait like

trait Factory[T] {
def createNew(): T
}

where a class T would provide an implicit value of Factory[T]. If
path-dependent types are desired, consider having a trait like

trait Factory {
trait T {
def createNew(): T
}
}

A class could now inherit like that:

class MyClass extends Factory {
class T extends super.T {
def createNew() = new T()
}
}

Another thing to keep in mind is that the .copy method for case classes
exists, which serves -- at least partly -- as a "constructor" for other
instances. The return type is not path-dependent, though.

Nate Nystrom 2
Joined: 2011-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inherited constructors and dependent types

I think inherited constructors could be added to Scala, but there are a lot of complications (e.g., how do you initialize fields added in a subclass), and as Lars just pointed out, you can use factories to work around not having them.

Rather than inherited constructors, Scala structural types could probably be extended with constructor signatures. Then, one could do:

def make[A <: AnyRef { def this(): Unit }] = new A()

This addresses all the use cases I can think of and should be simpler to integrate into the language than inherited constructors. But, I'm not convinced there are enough uses cases for this to justify the extension over just using factory objects. One can get pretty much the same behavior using context bounds:

trait Factory[A] { def createNew: A }
def make[A: Factory] = implicitly[Factory[A]].createNew

class C
implicit object CFactory extends Factory[C] {
def createNew = new C()
}
make[C]
res7: C = C@2bc1e78c

Nate

On Monday, June 20, 2011 at 14:36 , Lars Hupel wrote:

> Another feasible solution for this would be to use the type class
> pattern. (In Java terms, known as the Factory pattern.)
>
> In Scala, this is really easy to accomplish by having a trait like
>
> trait Factory[T] {
> def createNew(): T
> }
>
> where a class T would provide an implicit value of Factory[T]. If
> path-dependent types are desired, consider having a trait like
>
> trait Factory {
> trait T {
> def createNew(): T
> }
> }
>
> A class could now inherit like that:
>
> class MyClass extends Factory {
> class T extends super.T {
> def createNew() = new T()
> }
> }
>
> Another thing to keep in mind is that the .copy method for case classes
> exists, which serves -- at least partly -- as a "constructor" for other
> instances. The return type is not path-dependent, though.

Lars Hupel
Joined: 2010-06-23,
User offline. Last seen 44 weeks 3 days ago.
Re: Inherited constructors and dependent types

> def make[A <: AnyRef { def this(): Unit }] = new A()
>
> This addresses all the use cases I can think of and should be simpler
> to integrate into the language than inherited constructors. But, I'm
> not convinced there are enough uses cases for this to justify the
> extension over just using factory objects.

Me neither. The additional problem is that having this() in a structural
type introduces an asymmetry: All methods in Java/in the JVM are
dispatched dynamically (aka subtype polymorphism), but a constructor is
bound to a concrete class. And of course -- as always with structural
types -- the constructor has to be called via reflection (unless an
implicit factory method is passed into 'make' which is essentially the
same as using the factory pattern).

andrevandelft
Joined: 2010-02-07,
User offline. Last seen 34 weeks 1 day ago.
Re: Inherited constructors and dependent types

I am not happy with the proposed alternatives; they look too
complicated for what I want.

My use case is: I have a list of operations to operate on nodes in a
directed graph. Some operations typically propagate upwards. So I want
to write something like:

abstract class GraphAction extends Ordered[GraphAction] {
def node: GraphNode
def perform: Unit
def This(node: GraphNode) // abstract inherited constructor
}
abstract class UpwardsGraphAction extends GraphAction {
override def perform: Unit = node.parents.foreach(_ => insert(new
This(_))
}
case class SpecialAction(node: GraphNode) extends UpwardsGraphAction
{
// this case class implements its abstract inherited constructor
override def perform: Unit =
node match {
case n@SpecialNode => return
case … => …
}
super.perform
}
}

I am not sure whether this may be well defined in Scala; in Java it is
not hard. Almost a decade ago I added path dependent types and
inherited constructors to Sun's compiler. The type of 'this' became
'this.This', or 'This' in short. Consider the definition of type
'p.This' for a stable path p:

Let obj1 be the run time value of a variable or expression that has
type 'p.This', and let obj2 be the object referred to by the value of
'p'. Then obj1‘s class must be obj2’s class.

The new features would be useful for Java Collection classes. From the
API:

"All general-purpose Collection implementation classes (which
typically implement Collection indirectly through one of its
subinterfaces) should provide two "standard" constructors: a void (no
arguments) constructor, which creates an empty collection, and a
constructor with a single argument of type Collection, which creates a
new collection with the same elements as its argument. In effect, the
latter constructor allows the user to copy any collection, producing
an equivalent collection of the desired implementation type. There is
no way to enforce this convention (as interfaces cannot contain
constructors) but all of the general-purpose Collection
implementations in the JDK comply."

This restriction may easily be expressed using inheritable
constructors, and a "select" method may be added. Interface
Collection would get:

This();
This(Collection col);
This select(Predicate p);

In AbstractCollection this would become (ignoring immutable
collection issues):

public This() {}
public This(Collection col) {addAll(col);}

public This select(Predicate p) {
This result = new This();
for (Iterator iter=iterator(); iter.hasNext();) {
E element = iter.next();
if (p.evaluate(element)) {
result.add(element);
}
}

 return result;

 }

The following rules applied:

1. It is an error if the source text of a class contains both a
concrete inheritable constructor and a classic constructor with the
same parameters.

2. A concrete inheritable constructor, explicitly present in the
source text, implies a classic constructor with the same body,
parameters, thrown exceptions and visibility. That is, the implicit
classic constructor is not present in the source text, yet code is
generated as if such a constructor would have been present.

3. Consider a class C that inherits an inheritable constructor but it
does not have explicitly in its source text an implementation (either
classic or inherited) with the same parameters; C’s super class has a
concrete implementation of such a constructor (either classic or
inherited, and either implicit or explicit). Then C gets an implicit
classic constructor with the same parameters, exceptions and
visibility as the one in the super class; the body consists of an
invocation of the constructor in the super class.

4. A concrete class C that inherits an abstract inheritable
constructor T (either from an abstract class or interface) must have a
concrete implementation of a constructor with the same parameters as T
and a subset of the thrown exceptions.

4. The expression new obj.This(params)requires that the type of obj
has an inheritable constructor with appropriate parameters, either in
the class or interface denoted by the type or in the super class
hierarchy or in the interface hierarchy. The run time effect of such
an expression is that the corresponding classic constructor in the
class of the object referred to by obj is invoked.

Implementation: inheritable constructors are compiled into the same
code as classic constructors. Also, for each inheritable constructor,
an extra method is generated, named “This“ and with the same
parameters. This method invokes the inheritable constructor and
returns the result. In each subclass, the same constructor and the
extra method are generated as well, as far as not already defined
there.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland