Feature Proposal - Automatic Delegation

It never caught on but Adam Warski wrote an implementation of this a few years back with a blog post describing the implementation. http://www.warski.org/blog/2013/09/automatic-generation-of-delegate-methods-with-macro-annotations/

2 Likes

Well obviously if a wrapper does nothing more than delegation then it shouldn’t exist in the first place. I’m talking about cases where some of the functionality is completely delegated, while some is altered.

That’s the solution that was proposed in the linked SO question, but it’s not close to being a real delegator / wrapper / proxy. True though, it might very well be that this feature is too complex for its worth.

What properties makes a delegator/wrapper/proxy “real”?

I think it is a kind of dynamic dependency injection when:

  • we need to set trait’s implementation dynamically
  • we need be able to override some methods in this class

Nowadays we need to implement dynamic dependency injection manualy.

May be with trait parameters we will be able to do it more easily:

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

@proxy trait PArtist(impl: Artist)  extend Artist

class ConArtist(inspiration: Artist)  with PArtist(inspiration)

I think it would be good ability

Being able to decorate some of the methods instead of a full delegation (like the decorated name method in the original example).

You can still do that through shadowing though.

C:\Users\Martijn>scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_102).
Type in expressions for evaluation. Or try :help.

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.language.implicitConversions

class Art(val description: String) {
  override def toString() = s"about this work: $description"
}

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

class Painter(override val name: String) extends Artist {
  override def create(): Art = new Art("a beautiful painting")
}

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

object ConArtist {
  implicit def findInspiration(from: ConArtist): Artist = from.inspiration
}

val vermeer = new Painter("Johannes Vermeer")

val vanMeegeren = new ConArtist(vermeer)

// Exiting paste mode, now interpreting.

import scala.language.implicitConversions
defined class Art
defined trait Artist
defined class Painter
defined class ConArtist
defined object ConArtist
vermeer: Painter = Painter@6c4f9535
vanMeegeren: ConArtist = ConArtist@5bd1ceca

scala> vanMeegeren.name
res0: String = Johannes Vermeer the original

scala> vanMeegeren.create
res1: Art = about this work: a beautiful painting

scala>

So I guess I’m looking for a more rigourous definition of real delegator / wrapper / proxy. What code should compile/not compile for a real one, but not for an unreal one, or how would their observable behaviour differ. If their observable behaviour is the same, I suggest there isn’t actually a difference between a real one and an unreal one.

I’m not sure what decoration means, so maybe this lacks real decoration?

I think that it does not work with java reflection. So such components will be difficult to use in frameworks which cooperate with components through reflection.

Given that underscores are one of the more confusing things about Scala already, adding another meaning for them doesn’t seem like it would help that situation.

If this were to be a new language feature, you could have something like

class ConArtist(inspiration: Artist) extends inspiration {
  override def name: String = ???
}

which makes it pretty clear that it’s delegating to inspiration. OTOH, it further blurs the term/type lines by using a term in type position (I suppose it could be extends inspiration.type instead, but would imply that inspiration couldn’t be a var, which kills the dynamic use cases that I think the OP is talking about).

That said, I’ve wished for something like this on a handful of occasions, but given the language overhead I think it’s probably something better left to a compiler plugin. Which hopefully Scala 3 will still support?

2 Likes

There are actually multiple design patterns closely related here - delegator, decorator, adapter and facade. But let’s not lose track, I’m interested in the following behavior:

def introduce(artist: Artist): String = s"${artist.name} belongs in a museum"

val painter = new Painter("Bansky")
val conArtist = new ConArtist(painter)

scala> introduce(conArtist)
res0: String = Bansky the original belongs in a museum

Yes, for that you indeed need the feature you describe, or generate it through macros or code generation as far as I can see, though you still need subtyping, rather than composition, since def introduce(artist: Artist): String must be available through separate compilation, and scala won’t know that ConArtist must be able to be received by introduce when it compiles introduce.

I used to like using this pattern when I wanted to expose a subset of the functionality of an object in other langs. Say I have a class that exposes 40 methods, I only want to expose 4 of them to my codebase. I could use a typeclass but when langs support first class forwarding it becomes very concise to do
class onlyTheGoodStuff
delegates(method1, method2, method3) to GoodStuffPlusAllKindsOfQuestionableStuff

It’s not as flexible as other solutions, you can’t easily rename methods with this style, but it’s so cheap and easy I found it quite useful. Haven’t heard a name for it but it’s almost an undecorator pattern, don’t add stuff, take it away.

I think when we write library or facade it is no matter how we must do it.
I can not see a big deffrence between

class onlyTheGoodStuff
delegates(method1, method2, method3) to GoodStuffPlusAllKindsOfQuestionableStuff

or

class SomeClass ... {
  def method():Unit  = someImpl.method
}

It has very little influence in final costs.

But in everyday tasks when we create a special business logic, for example components for services or user forms, or tests etc.
It’s very important to have comfortable way to add functionality.
For example ( ScalaTest )

class ExampleSpec extends FlatSpec with Matchers {

It is ridiculous to create delegates manually in such case :slight_smile:

I didn’t see it mentioned anywhere on this thread, but this was already discussed as part of the discussion around adding an export keyword (delegation was one of the proposed semantics).

Also, I’m not in favor of a syntax that requires the object being delegated to to be a parameter of the class, which I find too restrictive. Why not allow delegation to any fields?

1 Like

Interesting. I’m not sure how to go about this. It seems as if the aforementioned proposal is a bit broader, isn’t it? Besides, I dislike the notion of an “inverted import”, which isn’t exactly the case here. The two capabilities serve completely different use-cases, and binding them to the same terminology is a wrong idea IMHO.

Sure, it would be useful to allow delegation to fields as well. @jeremyrsmith had mentioned this while addressing the potential ambiguity between terms and types.

It is very interesting question. There is another one:

So, It seems more clear to use inheritance closure.

I think " export, dual* of import" is very error prone method for dsl.
I would be happy if there is a way to escape any export\import keyword.

class SomeComponent extends DSL{
    //Any exports\imports must be implicit
   //It's possible by writing type aliases, 
  //but any new type we must add to "Dsl class" manualy
}

or

doSql{
    //Any exports\imports must be implicit
   //now it is imposible ((
}

See: Implicit Function Types - #38 by AMatveev

Etc.

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