I want to create a Scala Id typeclass, so that, for example, I can declare that an Id for type Foo takes a Long value, e.g.
val fooId: Id[Foo] = Id(12L) // type-safe at compile time
val fooIdValue: Long = fooId.value // able to get the value back out
I’ve tried various ways but I can’t seem to enforce the constraints. If I declare
trait WithId[I] {
type Id = I
}
case class Id[A <: WithId[_]](value: A#Id) // A#Id => Any, not what I want!
class Foo extends WithId[Long] {
type Id = Long
}
this allows
val fooId: Id[Foo] = Id("foo") // should be illegal unless a Long
If I change WithId to use an abstract type
trait WithId {
type Id
}
case class Id[A <: WithId](value: A#Id)
class Foo extends WithId {
type Id = Long
}
then
val fooId: Id[Foo] = Id(12L)
doesn’t compile, saying
no type parameters for method apply: (value: A#Id)net.box.Id[A] in object Id exist so that it can be applied to arguments (Long) --- because --- argument expression's type is not compatible with formal parameter type; found : Long required: ?0A#Id
How can I say and enforce that an Id[Foo] takes a Long?
You’re right to get rid of that type parameter on
WithId. However, the second problem that arises results from how you’re instantiating yourId. When you sayval x: Foo[T], you’re specifying what type you wantxto have, but you’re not really helping the compiler out with figuring out what typeFooshould use when it’s being constructed. So… the compiler error that you’re getting results from saying toFoothat you want theIdout ofA, but you haven’t toldFoowhatAeven is! To fix it, simply change your usage to