Hi scala-user,
I've found myself using a lot of path-dependent types for dependent-pair-like data structures, but it seems to require a lot of boilerplate. I'm interested to see if there's a more concise method.
For example, I have a type representing a type family (with upper bound):
trait ValueType {
type getType <: Value
}
trait Value { ... }
// Value subclasses defined elsewhere
And I want to represent a "typed value" as a small data structure. Ideally this would just be a case class like:
case class ValueTerm(valType: ValueType, term: valType.getType)
Of course, this doesn't work because the path-dependent type would need to appear in a subsequent parameter list; but, path-dependent types in constructors aren't supported anyway.
So I ended up doing this:
sealed trait ValueTerm {
val valType: ValueType
val term: valType.getType
}
object ValueTerm {
private[this] case class ValueTermImpl[V <: Value](
override val valType: ValueType { type getType = V },
override val term: V
) extends ValueTerm
def apply[V <: Value](valType: ValueType { type getType = V }, term: V): ValueTerm =
ValueTermImpl(valType, term)
def unapply(vt: ValueTerm): Some[(ValueType { type getType = vt.valType.getType }, vt.valType.getType)] =
Some((vt.valType: ValueType { type getType = vt.valType.getType }, vt.term))
}
Which works. It's manageable (indeed, I've been using it for a few weeks now), and don't get me wrong—I'm super thankful I have the capability to represent this at all.
But it's still 90% boilerplate. And for any data structures that use ValueTerm in an interesting way (for example, applying a typed predicate to the term under the constraints that their types match up),
I have to write similar-looking boilerplate. This is a pain when maintaining the code, though benefits of type safety still way outweigh the costs here IMO.
On top of that, I'm going to have to explain my code to non–Scala programmers. It'd be really nice if I didn't have to justify a bunch of boilerplate for what are pretty simple ideas.
And a solution using existential types similar in form to ValueTermImpl[_] of the above aren't acceptable—I need the increased flexibility of type members and I would rather not have to juggle both type parameters and type members around at once: that would introduce more boilerplate into client code which is worse.
Any ideas of how to accomplish this? Or maybe, is this sort of thing in/going to be in dotty?
Thanks,
Julian