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 your Id
. When you say val x: Foo[T]
, you're specifying what type you want x
to have, but you're not really helping the compiler out with figuring out what type Foo
should use when it's being constructed. So... the compiler error that you're getting results from saying to Foo
that you want the Id
out of A
, but you haven't told Foo
what A
even is! To fix it, simply change your usage to
val fooId = Id[Foo](12L)