Feature Proposal - Automatic Delegation

Remember such kind of feature was one on of the earliest features of Groovy. (defined using @Delegate annotation)

I think it could be (should be) something “semi-similar” in Scala, so that it should be some member which provides some implementation of some interface, and it should be marked for delegation. But from my understanding of “nature of Scala”, it should be somewhere near to import keyword (at lest this keyword I guess may induct right associations about meaning of such newly introduced construct). Also being inspired by private[pkg] construct, I would suggest something like this

  trait Artist {
    def name: String
    def genre: String
  }

  trait ArtistDelegator extends Artist {
    @import[Artist] def artistToDelegate(): Artist
  }

Where ArtistDelegator should be effectively the same to the following definition

  trait ArtistDelegator extends Artist {
    def artistToDelegate(): Artist

    abstract override def name: String = artistToDelegate().name

    abstract override def genre: String = artistToDelegate().genre
  }
1 Like

I want to share an example of a delegation nightmare that would be nice to solve in the real world.

Imagine that you need to implement a JDBC connection pool. You need to manage the opening and closing of low level JDBC connections provided to you by an underlying driver, but not actually give those connections back to the user because they need to be forbidden from taking certain actions, or these actions need to be intercepted, modified, or tracked so that state can be returned to normal when the connection is returned to the pool.

https://docs.oracle.com/javase/7/docs/api/java/sql/Connection.html

There are over 50 methods to delegate to!

Many of them are pass-through. I want to only override the subset that need modification and not touch the ones that are pass-through.

This example is not perfect (in reality we probably need to override all of them to fail once close() is called), but it is illustrative.

One can use existing frameworks to handle this, like Mockito, or JVM proxies, but I’d rather have the compiler do it for me, and not some reflective runtime tool.

2 Likes

I like the idea of having such feature in language but this problem seems to be simple to fix using macros. I thought about it some time ago when I wrote a lot of such proxy wrappers by hand.

trait Artist {
  def name: String
  def create(): Art
}

class Painter(override val name: String) extends Artist {
  override def create(): Art = ???
}

class ConArtist(@delegate inspiration: Artist) extends Artist {
  override val name: String = s"${inspiration.name} the original"
}

there is old and incomplete Proof of concept of such macro-annotation library (even don’t know if it still compile). https://github.com/adamw/scala-macro-aop

1 Like

I have came across exactly this problem as well. Ended up developing a macro annotation.
It does exactly what you describe.

For future reference: GitHub - cmhteixeira/delegate-macro: Annotation to automatically delegate/proxy implementation of interface to dependency
It is cross compiled to 2.11, 2.12, and 2.13.

The only disadvantage is that you need to import a compiler plugin. For 2.13 that is easy, you just set the flag “-Ymacro-annotations”. For 2.11 and 2.12 it is slightly trickier. Instructions on the repo.

2 Likes