I have an Animal trait and a few case classes as follows
sealed trait Animal
trait Domestic extends Animal
trait Wild extends Animal
case class Dog(id: UUID = UUID.randomUUID()) extends Domestic
case class Lion(id: UUID = UUID.randomUUID()) extends Wild
Here is my Herd class which can contain a list of a single type of animals
case class Herd[T <: Animal](get: T*)
I want to create is a herd of a single type of animal.
val herd1 = Herd(Cat(), Cat())
val herd2 = Herd(Cat(), Lion())
In Scala both are valid, but if you look at the meaning a herd of cats and lions it doesn't make sense. Is there a way to restrict Herd to be a single type?
Try introducing two type parameters
AandBand then relate them with a generalised type constraintA =:= BConsider the following method with two type parameters
AandBwhere we aim to convey that they should be equal or at leastAshould be a subtype ofBThe two type parameters are totally unrelated in the above definition and the method body cannot influence the type inference of type parameters so it errors. Now let's try to relate them with a type bound
A <: BSo this compiles, however compiler will always try to satisfy the type bounds by calculating the least upper bound of the given arguments
We need something more to get around compiler's tendency to deduce a least upper bound, and this is where generalised type equality constraint comes into play
Now compiler still has to try to generate the least upper bound of given arguments, however it also has to satisfy the additional requirement of being able to generate the witness
evfor two typesAandBbeing equal. (Note the witnessevwill be automagically instantiated by the compiler if it is possible.)Once we have the witness
evwe can freely move between typesAandBvia itsapplymethod, for example, considerNote how
ev.apply(a)types toB. The reason we can apply=:=in this way is because it is actually a functionso the implicit parameter list
is in effect specifying an implicit conversion function
so now compiler is able to inject automatically implicit conversion wherever it is needed so the following
is automatically expanded to
In summary, just like type bounds, the generalised type constraints are an additional way of asking the compiler to do further checks at compile-time on our codebase.