In Scala 2, there is nothing, and it seems like pretty questionable design to me anyway. If a Tracks is just a Vector[Track] then I wouldn’t have Tracks at all, or maybe type Tracks = Vector[Track].
And if Tracks is something more than just that — if it has other methods — then requiring users to add .tracks to get at the methods on Vector seems just fine to me. Desirable, even.
Yes, Tracks has other methods that I did not show.
I just did a quick test of export, and it seems to do exactly what I asked for if I just add
export tracks._
Nice! I’ll try not to abuse it.
By the way, one reason this feature is significant to me is that it allows me to essentially replace all occurrences of Vector[Track] with Tracks in my code without making any other changes. That not only eliminates work, but it also reduces the chances of an error.
After working with it a bit, I now realize that “export” does not quite do exactly what I wanted, but it’s still a step forward.
For basic Vector methods like “isEmpty”, “length”, etc., it does exactly what I want. But for methods like “take”, “drop”, etc., it returns a Vector[Track] rather than a Tracks instance. That should have been obvious, I guess.
If we allow for extremely questionable design, there’s implicit conversions. Just mentioning it for the sake of completeness, don’t try this at home.
In code blocks where the member is accessed often, it can be assigned to a dedicated val or imported to avoid repetitive use of the accessor.
If your other methods do not require additional fields, i.e. the Vector[Track] is the only thing stored in that class, you could maybe use extension methods instead? (as you can use export, I assume you’re using dotty)
So instead of wrapping the Vector, you make your methods available on all Vectors with the right element type, which solves the problem of Vector methods returning Vector.
The decorator pattern usually is about extending the implementation of some well-defined, closed API (Component in the Wikipedia page), rather than just exposing any method available on a wrapped object. And for this use case Scala provides the stackable traits feature/pattern.
I thought your suggestion to use “extension” might be the answer. However, I tried it and was disappointed.
The problem is that the namespace is apparently shared with other Vector extensions. So if I have a Tracks = Vector[Track] and Things = Vector[Thing] (made up name), I can’t use the same method name in the two of them. I did not expect that, since I thought that Vector[Track] and Vector[Thing] were two different types. I could put them inside companion objects, but then I have to use imports or qualified names, which is not as elegant.
Also, I wanted a toString method, and for some reason the compiler wouldn’t let me add that.
Extension seems like a nice idea, but I had other problems with it as well when I tried to convert some some of my implicit classes to extensions. I certainly hope implicit classes are not going away unless or until extension can do everything they can do.
scala> class A; class B
// defined class A
// defined class B
scala> extension (as: Vector[A]) def foo = 3
def foo(as: Vector[A]): Int
scala> extension (bs: Vector[B]) def foo = "bar"
def foo(bs: Vector[B]): String
scala> Vector(new B).foo
val res0: String = bar
scala> Vector(new A).foo
1 |Vector(new A).foo
|^^^^^^^^^^^^^^^^^
|value foo is not a member of Vector[A].
|An extension method was tried, but could not be fully constructed:
|
| foo(Vector.apply[A]([new A() : A]:A*))
I’m curious if this is considered to be working-as-designed, or whether it could be changed.
Although “extension” has problems as noted above, I am finding that implicit classes can be used to solve this problem fairly nicely. (But it requires the use of the “implicit” keyword, which might freak some people out!)
That leaves me wondering what the relationship is between extension and implicit classes. Although extension apparently cannot be used to add data fields, it seems to serve the same purpose as implicit classes otherwise.
Is extension intended to eventually replace implicit classes? If so, it still needs some work.
implicit classes serve a single purpose, adding extension methods.
The extension keyword was added in Scala 3 to provide proper first-class support to that use case.
That’s what I thought, but I would still like to know if “extension” is ultimately intended to replace implicit classes. If the answer is yes, then it needs more work.
Here is another question regarding both extension and implicit classes. The example given for extension methods is
case class Circle(x: Double, y: Double, radius: Double)
extension (c: Circle)
def circumference: Double = c.radius * math.Pi * 2
Why does the Circle that is being extended even need a name? Why can’t it just be
with an implied name of “this”, as usual? That would make the code under the extension identical to code in the original class, which would promote consistency.