Proposal to introduce super: Type => (to simplify delegations)

Actually idea is that I would write some “decorator” in form of trite, and probably use keyword super instead of some delegate() method (from base class/trait). Honestly while I was start learning Scala I’ve strong believe that it is possible by the mean of regular traits inheritance … (but when I’ve tried to write that, I understood that it is impossible). When I’ve learned self-types, I’ve got an idea that something similar may help in this problem.

So in general, idea was in somethin like, instead of writing something on top of ForwardingCollection and overriding/decorating some methods of interest, I would probably prefer to write something just on top of
Collection , in form of trait with “super-self-type” like

trait MyCollectionDecorator[T] {
  super: java.util.Collection[T] =>

  override def add(e: T): Boolean ={
    println(s"onAdd: $e")
    super.add(e)
  }
}

By writing this I would expect that MyCollectionDecorator almost regular “self-typed” trait, but it would not only require that final implementor mixed it with Collection[T], but making some stronger requirement, that implementor should mix it wit some valid implementation of Collection[T] and that implementing trait/class should stand erectly somewhere “before” MyCollectionDecorator[T] in linearized inheritance order.
So that it should be valid construct new ForwardingCollection[T] with MyCollectionDecorator[T] but
writing it in opposite direction - new MyCollectionDecorator[T] with ForwardingCollection[T] should cause compile time error.

In fact, most “expressive” snippet which I can imagine, may look even like this

trait MyJavaListDecorator[T] {
  super: {def get(index: Int): T} =>

  override def get(index: Int): T = {
    println(s"onGet: $index")
    super.get(index)
  }
}

And then again, I would expect that new ForwardingList[T] with MyJavaListDecorator[T] will compiles fine while new MyJavaListDecorator[T] with ForwardingList[T] reports compilation error.

And also, I should mention, that “partially” this feature is currently working, in particular, with Object.toString
method (in fact, same as with any other Object’s methods)

  trait WrapToString {
    override def toString: String = "WrapToString: " + super.toString
  }

- this code works correctly even now, but again, it would be nice to “refine” our knowledge about super in some
indirect way (not only by explicit mentioning that “knowledge” in extends section)

By the way, current assumption that super implements all methods of Object (AnyRef) in general is false, since it can be violated by overriding toString with abstract method, so the following code reproduces that scala compiler minor bug

package crafts

object Main {

  def main(args: Array[String]): Unit = {
    val obj = new Combined
    println(s"obj: ${obj}")
  }

  class Combined extends SuppressToString with WrapToString

  abstract class SuppressToString {
    override def toString: String
  }

  trait WrapToString {
    override def toString: String = "WrapToString: " + super.toString
  }

}

- while it is quite obvious that SuppressToString class is abstract, and it’s toString method is abstract
(and need to be “reimplemented”) compiler does not reject following mixture SuppressToString with WrapToString and assumes that it is alright with that type. As the result, code compiles but predictably fails on runtime with following error:

Exception in thread "main" java.lang.AbstractMethodError: java.lang.Object.toString()Ljava/lang/String;
	at crafts.Main$Combined.crafts$Main$WrapToString$$super$toString(Main.scala:10)
	at crafts.Main$WrapToString.toString(Main.scala:17)
	at crafts.Main$WrapToString.toString$(Main.scala:17)
	at crafts.Main$Combined.toString(Main.scala:10)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at crafts.Main$.main(Main.scala:7)
	at crafts.Main.main(Main.scala)

So, from this perspective I may also assume that it can be imaginary semantic extension, that all method from this: Type => construction can be automatically visible on super as well as on this (if they are missing in types from explicit extends section), but then compiler should carefully control that all that super-method usages will be properly validated when making actual mixture of that trite with other trites

As far as I can see, what you want is the following (syntactically strange but working) feature of Scala: abstract override def. To make trait to be obliged to be mixed with a particular class, you need (again, syntactically strangely) to have your trait to extend the desired class.

Regarding to your example, you can easily write something like

trait MyCollectionDecorator[T] extends java.util.Collection[T] {
  abstract override def add(e: T): Boolean ={
    println(s"onAdd: $e")
    super.add(e)
  }
}

and when you use it like

val coll = new java.util.HashSet[Int] with MyCollectionDecorator[Int]
coll.add(45)

then

onAdd: 45

will be printed as you want.

1 Like

Interesting, I remember that last time when I try something similar to what you wrote, I’ve got some compile time error. But now when I try your snippet, it fortunately works.

Than it looks that the only (minor) problem still remains, that when I suppress some build-in Object’s method (such as toString, equals, hashCode, …) and use that methods in trait in super. position compiler cannot detect error. (bug #11075)

also “almost equivalent” snippet with sef-types does not work

  trait MyCollectionDecorator[T] {
    this: java.util.Collection[T] =>
    abstract override def add(e: T): Boolean = {
      println(s"onAdd: $e")
      super.add(e)
    }
  }

Maybe practically not very important notice, but still, from my current understanding of super in traits, I would expect that it should also be correct snippet

No, because when you have

trait A { self: X =>
  ...
}

it doesn’t mean that A <: X, it only means that any non-abstract class that inherits A must also inherit X. That’s why it is not hold that super.isInstanceOf[X] inside A.

As for me it can be interpreted differently. From my simplified underspending this: T => is actually that construction that allows us to overcome extends limitation (such as class Foo[T] extends T {} is invalid). So practically I would expect from this: T => almost everything what I can expect from extends T (at lest in the body of corresponding trait).

In fact trait MyCll[T] extends Collection[T] definition itself also does not impose a limitation on super - that super mast “point” to some valid implementation of Collection[T], this limitations are inferred from trait’s body.

That is why I think the same rule could be used (should be used) for this: T => - if the trait’s body will contain some usages of “super as instance of T” (some methods available in T are called from super.) then some
implicit (inferred) limitations should be collected and verified in place of mixing (instantiating) of that trait with appropriate implementers (just the same as it works for extends T construction)

I generally agree with you but I really feel that usage of term super for denoting to “those what this trait is mixed in” is incorrect because trait mixing in is somewhat horizontal but inheritance is somewhat vertical. super means “going up vertically”.

But to pretty same feelings to yours and to see one of previous discussions of this issue, you can see this topic.