This demonstrates an approach to shared objects, and shows how to work with abstract types as well. Note that this is a work-in-progress posted for comment!
package scalax.shared; /** We provide a default shared factory object. This code is in the public domain. @author Ross Judson */ object Shared extends SharedFactory { } /** A SharedFactory groups shared objects. Once shared, an object will only equal itself. Shared objects are not equal between factories. */ class SharedFactory { import java.util.WeakHashMap; import java.lang.ref.WeakReference; private val objects = new WeakHashMap(); private var id = 0; /* def build[T <: SharedObject](proto: T): T = synchronized { val bo = new BoxedObject(proto); val current = objects.get(bo).asInstanceOf[WeakReference]; var ret: T = null; if (current != null) { ret = current.get().asInstanceOf[T]; } if (ret == null) { proto.setFactory(this); objects.put(bo, new WeakReference(proto)); ret = proto; } ret } */ def freshID = synchronized { id = id + 1; id } } /** Log prints a message when hashCode or equals is called. */ trait Log { abstract override def hashCode() = { Console.println(tag + ".hashCode"); super.hashCode() } abstract override def equals(o: Any) = { Console.println(tag + ".equals"); super.equals(o); } def tag: String = getClass().getName(); } /** LogTag extends Log with a custom message instead of printing the class name */ trait LogTag extends Log { val logTag: String; override def tag = logTag; } /** FixedHash modifies a class to compute an initial hash value then quickly return the precomputed hash from then on. */ trait FixedHash { private val hash = super.hashCode(); Console.println("Computed Fixed hash: " + hash); abstract override def hashCode() = hash; abstract override def equals(o: Any) = o match { case fh: FixedHash => hash == fh.hash && super.equals(o) case _ => false } } /** A SharedObject is a unique object within a SharedFactory for a given value. */ trait SharedObject { /** Returns the factory this shared object is a member of. */ val factory: SharedFactory; /** We override equals to use direct object identity. */ abstract override def equals(o: Any) = o.isInstanceOf[AnyRef] && (this eq o.asInstanceOf[AnyRef]); /** Use the superclass to determine if we are equivalent to another object. */ def equivalent(o: Any) = super.equals(o); } /** A Shareable object is an object that can be shared -- this is the "natural" state for the object. When duplicate is called, implementers must return a subclass that further implements FixedHash with SharedObject. */ abstract class Shareable { // Use an abstract type to identify the self type type This <: Shareable; // Specify the lower bound on the shared type produced by duplication type SharedType = This with FixedHash with SharedObject; // private[shared] unnecessary and unwanted here -- just trying it out. private[shared] def duplicate(f: SharedFactory): SharedType; private[shared] def duplicate: This = duplicate(Shared); } /** Some test code. */ object Test extends Application { abstract class Term extends Shareable; case class Name(name: String) extends Term { override type This = Name; // Build shared object with LogTag support. Note that // "with FixedHash with SharedObject" // satisfies the signature for duplicate; we are further blending in the // logging support that we want. private[shared] def duplicate(f: SharedFactory) = new Name(name) with FixedHash with SharedObject with LogTag { val factory = f; val logTag = "SharedName"; } } case class Func(name: String, arity: int) extends Term { override type This = Func; // Build shared object with regular Log. private[shared] def duplicate(f: SharedFactory) = new Func(name, arity) with FixedHash with SharedObject with Log { val factory = f; } } // It doesn't look like this will work. We don't get the abstract override // behavior we want. trait FSH extends FixedHash with SharedObject with Log; case class Var(name: String) extends Term { override type This = Var; private[shared] def duplicate(f: SharedFactory) = new Var(name) with FSH { val factory = f; } } val nameRoss = Name("Ross"); val funcRoss = Func("Ross", 1); val varRoss = Var("Ross"); p(nameRoss); p(funcRoss); p(varRoss); def p(t: Term) = { val dup = t.duplicate; pp(t); pp(dup); } def pp(t: AnyRef) = Console.println(t.getClass().toString() + " ==> " + t.toString() + ':' + t.hashCode()); }