trait Entity:
type Key
object Entity:
type Aux[K] = Entity { type Key = K }
// match type
type EntityKey[T <: Entity] = T match
case Entity.Aux[k] => k // k being lower-case is significant
type Dictionary[T <: Entity] = Map[EntityKey[T], T]
I had to introduce Aux
-type because match types seem not to work with refined types
type EntityKey[T <: Entity] = T match
case Entity { type Key = k } => k // Not found: type k
Beware that on value level match types can work not so well as type projections: Scala 3: typed tuple zipping
- Besides match types, also type classes can be an alternative to general type projections
trait Entity:
type Key
// type class
trait EntityKey[T <: Entity]:
type Out
object EntityKey:
type Aux[T <: Entity, Out0] = EntityKey[T] { type Out = Out0 }
given [K]: EntityKey.Aux[Entity { type Key = K }, K] = null
// replacing the type with a trait
trait Dictionary[T <: Entity](using val entityKey: EntityKey[T]):
type Dict = Map[entityKey.Out, T]
What does Dotty offer to replace type projections?
https://users.scala-lang.org/t/converting-code-using-simple-type-projections-to-dotty/6516
Dotty cannot infer result type of generic Scala function taking type parameter trait with abstract type
Map[Any, T]
in Scala 2; which is why it was removed. It wasn't doing anything useful at all. – WenonawenonahEntity
type and itsKey
type? How come there is no way to express such a simple constraint? – CastaraKey
can be anything. – WenonawenonahDictionary
,Key
depends on actualT
type. But I get your point: there is no guarantee thatKey
is actually defined unlessT
is a concrete type. – CastaraT
is a concrete type,Key
may be still undefined. - Or, it may be defined asAny
. - Or may be defined in a way such that the actual type can change for each instance. – WenonawenonahKey
is actually defined unlessT
is a concrete class". – CastaraT
is a concrete class: TheKey
type may not be defined - It may be defined asAny
- Or, it may be defined in a way that the actual type will depend on each instance. – WenonawenonahA
that would implementEntity
trait and therefore defineKey
type would not put any constraint onDictionary[A]
(type Key = Any
would be, of course, a very loose constraint!). – Castaratype T = Foo
that is actually just sugar syntax fortype T >: Foo <: Foo
– Wenonawenonahtype T
it actually meanstype T >: Nothing <: Any
so that is why that projection is mostly equivalent to justAny
– WenonawenonahMap[Any, T]
in Scala 2" AlthoughMap[T#Key, T]
can be similar toMap[Any, T]
at runtime, at compile time they are quite different. You can always putstring -> t
intoMap[Any, T]
but apparently you can not always put it intoMap[T#Key, T]
. – NealyL
andU
are the same in implementations oftype T >: L <: U
, this can be checked with a macro (at least for simpleL
andU
like intype T >: Foo <: Foo
). – NealyT = Foo
that is actually just sugar syntax for typeT >: Foo <: Foo
" This is a syntax sugar in DOT, but not literally a syntax sugar in implementations for performance reasons (although intended to be the same) scastie.scala-lang.org/Hl3ykwYNRTujc2BpESyy8A contributors.scala-lang.org/t/whats-in-a-type-alias/4001 – Nealy