The aesthetics are however a bit challenging - either you use an F-bounded type, Pet[A <: Pet[A]], which can "leak", or you use a TypeClass, which leads to a lot of boilerplate for each specific class.A common question on the
#scala
IRC channel isI have a type hierarchy … how do I declare a supertype method that returns the “current” type?
class StringBuilder {
def append(s:String):This = {
//...
this
}
}
trait Fancy {
self: StringBuilder =>
def fancy(s:String):This = {
//...
append(s)
}
}
class FancyStringBuilder extends StringBuilder with Fancy {
}
val f = new FancyStringBuilder()
f.append("hello").fancy("world!")
val f:StringBuilder = new FancyStringBuilder()
f.append("hello").fancy("world!") // => compilation error, StringBuilder doesn't have fancy()
val f = new FancyStringBuilder()
f.append("hello").asInstanceOf[FancyStringBuilder].fancy("world!")
trait Pet {
def name: String
def renamed(newName: String): This
}
final case class Fish(name: String, age: Int) extends Pet {
def renamed(newName: String): This = copy(name = newName)
}
I also feel it would be very useful to provide a more convenient syntax for F-bounds. Most importantly, traits with F-bounds wouldn't be needlessly generic anymore, spreading existential types everywhere. But to accomplish this, we really need to allow This everywhere the F-bound argument can be used today: in method argument types, field types, type arguments, etc. And why restrict it?
To maintain backward compatibility, it could be named something like this.subtype
.
The spec seems simple:
this.subtype
, it behaves as some unknown subtype of the current type.this.subtype
when viewed from A has some unknown subtype of A.But the implementation would require new info in the compiled class.
--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.
They discuss various alternatives, but in the end the OP settles for F-bounds. And we know from experience that F-bounds are useful and are often used, and that the introduced genericity (and verbosity) are harmful. A syntax deliberately equivalent to F-bounds would be simple to specify and to understand.
The problem is how to represent this in the Scala type system. F-bounds can’t be implemented with abstract type members instead of type arguments:
trait Tr { self =>
type Self <: Tr { type Self <: self.Self }
}
We cannot say here self: Self
as we would in the equivalent type-argument situation. So even if we enforce this at compile time, other users of the type wouldn’t know about it.
We could have the new syntax be equivalent to this:
trait Tr[F <: Tr[F]] { self: F =>
def copy(): F
}
object Tr {
type Trx = Tr[F] forSome{ type F <: Tr[F] }
}
import Tr.Trx
class C extends Tr[C] {
override def copy(): C = new C
}
// Use site
val tr: Trx = new C
val copy: Trx = tr.copy()
This much can be generated with a macro annotation today, and may even be worth writing by hand when we use F-bounds. If Tr
has extra generic arguments, then so will Trx
.
C still has to extend Tr[C], but that’s not a big a problem IMO. Everyone else needs to use Tr.Trx and not Tr, which is annoying but less annoying than using Tr[_] (which would have made the code above not compile).
What am I missing? There must be something, or this would already by accepted practice for F-bounds.
Ryu, S. (2016). ThisType for Object-Oriented Languages. ACM Transactions on Programming Languages and Systems, 38(3), 1–66. http://doi.org/10.1145/2888392
Ergonomically (read syntactically), I'd probably prefer:
this.type & this#type
--
Cheers,
√