Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

support "proper" delegation pattern #238

Closed
unkarjedy opened this issue Sep 3, 2021 · 4 comments
Closed

support "proper" delegation pattern #238

unkarjedy opened this issue Sep 3, 2021 · 4 comments

Comments

@unkarjedy
Copy link

unkarjedy commented Sep 3, 2021

See https://www.wikiwand.com/en/Delegation_pattern

The current export clause feature allows to achieve it only partially / not smoothly.
Currently, there are two options with export:

  1. write a wildcard, excluding all unrelated members export fieldDelegateTo.{unrelatedMember => _, _}
  2. list all methods from the trait export fieldDelegateTo.{foo1, foo2, ... fooN}

The first option is not only a boilerplate, it has a high risk of exporting too many unrelated public members.
You have to explicitly list all members to "mute". You can easily skip some.
If a new member is added to the class, most likely you will forget to update all the exports.
It may lead to context pollution.
It will potentially increase compilation time, show a lot of unexpected members in the completion list in IDE, etc...

The second option is also a boilerplate code.
You have to list all the members.
You can accidentally export some unrelated member.
You have to update all exports in case a new method is added to the trait.

Without "proper" delegation, export feels a little bit incomplete.
The motivation for export was to make "composition over inheritance" require less boilerplate code.
It's very likely that wildcard import will be overused (cause programmers are lazy), leading to all the cons listed above.

We need some new syntax, which would allow us to only export methods of a specific trait.
Something like export delegate.{_: MyTrait1}:

trait MyTrait1 {
  def traitFoo: String
}
trait MyTrait2 {
  def traitBar: String
}
class A extends MyTrait1 with MyTrait2 {
  override def traitFoo: String = ???
  override def traitBar: String = ???
  def unrelatedMethod1: String = ???
  def unrelatedMethod2: String = ???
}
class B extends MyTrait1 with MyTrait2 {
  private val impl = new A()
  export impl.{_: MyTrait1, _: MyTrait2}
}

From the first glance, this feature doesn't require a lot of effort (patch import selector parsing and restrict filtering of exported methods in Namer.scala).
The most difficult is as always to agree on the syntax.

@unkarjedy
Copy link
Author

related feature requests:

@unkarjedy
Copy link
Author

@smarter
Copy link
Member

smarter commented Sep 7, 2021

export impl.{_: MyTrait1}

  • Wildcard imports use * now so I assume this should be {*: MyTrait1}
  • In any case it looks like it's exporting values of type MyTrait1 rather than members of MyTrait1.

I think this was discussed before on contributors but my preferred syntax would be:

export (impl: MyTrait1).*

which would be roughly equivalent to:

val impl1: MyTrait1 = impl
export impl1.*

except it should retain path-dependency through impl

@unkarjedy
Copy link
Author

In any case it looks like it's exporting values of type MyTrait1 rather than members of MyTrait1.

Agree, the proposed syntax export impl.{_: MyTrait1, _: MyTrait2} is little confusing

@lampepfl lampepfl locked and limited conversation to collaborators Jun 5, 2023
@ckipp01 ckipp01 converted this issue into a discussion Jun 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants