0

Version 0.9.6 of Squeryl introduces a new way to declare classes that have an associated primary key, through the use of the KeyedEntityDef typeclass. Still the old way to declare

import org.squeryl.KeyedEntity

case class Foo(id: Long, myField: String) extends KeyedEntity[Long]

is supported.

I am trying to migrate an existing application that makes use of Squeryl 0.9.5 to the new version, to make use of custom primitive types, and I am facing a compilation problem. Here is an example of a trait that does not compile anymore

trait Retrievable[A <: KeyedEntity[Long]] {
  def table: Table[A]

  def get(id: Long): Option[A] = inTransaction {
    table.lookup(id)
  }
}

It was meant to be used like this:

case class Foo(id: Long, myField: String) extends KeyedEntity[Long]

object Foo extends Retrievable[Foo] {
  def table = DB.something
}

...

val foo = Foo.get(235)

Now, when I try to compile, I get the message

The method requires an implicit org.squeryl.KeyedEntityDef[A, Long] in scope, or that it extends the trait KeyedEntity[{K}]

although A does extend KeyedEntity[Long]. Even adding an implicit in scope, like

trait Retrievable[A <: KeyedEntity[Long]] {
  def table: Table[A]
  implicit val ev: <:<[A, KeyedEntity[Long]]

  def get(id: Long): Option[A] = inTransaction {
    table.lookup(id)
  }
}

does not help the implicit resolution, and the trait fails to compile.

Does anyone have a clue why the compiler is not feeding the implicit in the lookup method?

Andrea
  • 20,253
  • 23
  • 114
  • 183

1 Answers1

4

The signature for the lookup method was changed so that it accepts the KeyedEntityDef as an implicit parameter. For backwards compatibility, there is a KeyedEntityDef available for the KeyedEntity type. It's found in QueryDsl (see the kedForKeyedEntities implicit method), and is meant to be imported into scope as part of the "TypeMode" (i.e. PrimitiveTypeMode) that you're using. The quick answer is that you have two choices:

  • Make sure PrimitiveTypeMode._ is in scope where your Retrievable trait is defined
  • To make it more flexible, have your get method accept the same implicit parameters lookup does def get(id: Long)(implicit ked: KeyedEntityDef[T,K], dsl: QueryDsl): Option[A] and then pass them through table.lookup(id)(ked, dsl). That will push their resolution off until your get method is called and allow it to be used with whatever custom TypeMode you define.
Dave Whittaker
  • 3,102
  • 13
  • 14
  • Thank you. I tried both solutions you mention. As for the first one, `PrimitiveTypeMode._` was already there, but does not help. I think this has to do with the fact the implicit `KeyedEntityDef[Long, _]` would have to be produced by implicit conversion from an implicit `ev: A <:< KeyedEntity[Long]`, and I am not sure that Scala applies implicit resolution twice. I also tried the second method, but whenever I use `get` I obtain the error... – Andrea Dec 19 '12 at 08:34
  • `both method stringCanBuildFrom in object Predef of type => scala.collection.generic.CanBuildFrom[String,Char,String] and method kedForKeyedEntities in trait QueryDsl of type [A, K](implicit ev: <:<[A,org.squeryl.KeyedEntity[K]], implicit m: Manifest[A])org.squeryl.KeyedEntityDef[A,K] match expected type forSome { type _$1 }` – Andrea Dec 19 '12 at 08:35
  • My fault, your second solution works fine. I had not imported `KeyedEntityDef` in the scope of trait `Retrievable`, but the error was lost in a long list of error messages. – Andrea Dec 19 '12 at 08:48
  • Ah, for the first one, the issue isn't that the implicit needs to be resolved twice.... it's that the kedForKeyedEntities method in QueryDsl requires a Manifest. No Manifest can be produced for A at compile time, so compilation fails. The second solution was the better one anyway :) – Dave Whittaker Dec 19 '12 at 15:12