The type mismatch is a bit easier to see when you expand the context bound shorthand. Remember, the [T : Entity]
syntax is just syntactic sugar for requiring an implicit Entity[T]
to be in scope. So the header of your makeTable
function is actually:
def makeTable[T](implicit entity: Entity[T]): Table[T] =
Now with that implicit parameter in scope, your implicitly
call in the function will grab that value (actually, the new Table[T]
constructor grabs the implicit, and then implicitly
grabs it from there), so the makeTable
function in its expanded form:
def makeTable[T](implicit entity: Entity[T]): Table[T] =
new Table[T] {
def store(entity: T): Unit = {}
def retrieve(id: Entity[T]#Id): Option[T] = {
val key = entity.keyFromId(id) // Type mismatch on 'id'.
None
}
}
Notice that there is no conversion taking place for the type declaration of the id
parameter of retrieve
. Essentially, the compiler is complaining that entity.keyFromId
is expecting a parameter of type entity.Id
, but your id
parameter is of the abstract type Entity[T]#Id
.
The problem here is that you want to hide the Entity
and the ids from users of the Table
class, yet you want to expose the type of the Id
in one of the methods. Suppose I have two instances of Table[T]
, but they use different Entity[T]
implementations -- so different that their Id
types are actually different. How would the users of the table instances know which type to pass to retrieve
functions? You could fix this by adding another type parameter to the Table
class. You can still abstract away the id/key generation logic, but this will allow the compiler to type check to make sure the retrieve
function is getting the right type of parameter.