I would like to propose a new feature to the language - automatic delegation.
I very much favor composition over inheritance and therefore I create a lot of wrappers via the delegation design pattern, and in many of those wrappers I end up needing to alter the implementation of only a selected set of methods.
I would have liked to have a built-in feature in the language that saves me the time and effort of re-defining methods I don’t intend to change but only to delegate as is. Something like this:
trait Artist {
def name: String
def create(): Art
}
class Painter(override val name: String) extends Artist {
override def create(): Art = ???
}
class ConArtist(inspiration: Artist) delegates Artist using inspiration {
override val name: String = s"${inspiration.name} the original"
}
It is worth taking a look at a this stack-overflow question. The proposed solution there for the problem is quite interesting, but feels a little iffy and incomplete.
I think if wrappers do nothing except delegation it’s not good. It’s just reduce coupling . https://en.wikipedia.org/wiki/Coupling_(computer_programming).
But we also need delegation\proxy handlers for aspect oriented programming. Nowadays we use code generation for that purpose . It may be better to use macros.
I am interesting will it work:
trait Artist {
def name: String
def create(): Art
}
@Audit trait AuditArtist extend Artist {
@using
val inspiration: Artist
}
class ConArtist(inspiration: Artist) with AuditArtist
With white box macros we can generate method implementation in AuditArtist.
Will it be posible in dotty?
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.
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)
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
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?
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
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?
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 ((
}