6
import scala.slick.driver.MySQLDriver.simple._

class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {

  case class QueryExt[B](q: Query[RichTable.this.type, B])  {
    def whereEq[C](col: RichTable.this.type => Column[C], c: C) = {
      q.filter { fields =>
        col(fields) === c
      }
    }
  }

}

Then it complains

[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:10: value === is not a member of slick.driver.MySQLDriver.simple.Column[C]
[error]         col(fields) === c
[error]                     ^
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:9: ambiguous implicit values:
[error]  both value BooleanColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Boolean]]
[error]  and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error]  match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error]       q.filter { fields =>
[error]                ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed Mar 6, 2014 1:21:48 AM

There have been questions about this, but the answers did not work for 2.0

How to parametrize Scala Slick queries by WHERE clause conditions?

Community
  • 1
  • 1
jilen
  • 5,633
  • 3
  • 35
  • 84

1 Answers1

7

Slick doesn't have any information about C, so it doesn't know if it can and how it should map it to a database value and if it can use === on it. So you get a type error. You will have to use Scala's type system to restrict the type to one for which Slick knows how to map it. You can do this by providing a so-called Context Bound, in this case :BaseColumnType.

def whereEq[C:BaseColumnType](col: RichTable.this.type => Column[C], c: C) = {
  q.filter { fields =>
    col(fields) === c
  }
}

BaseColumnType is provided by Slick and using it in this way basically tells the Scala compiler to look for an implicit value of type BaseColumnType[C] in scope, where you call whereEq. Because then it is usually known what C will actually be. Slick comes with BaseColumnType[Int], BaseColumnType[String], etc. so at the call site, the Scala compiler can find one when your C is really an Int or String in that particular call and this way pass the info further to Slick.

Same for LiuTiger's question. abstract class Crud[..., PK:BaseColumnType] should do the trick, a trait doesn't work with context bounds. When implementing an abstract DAO be prepared to face a lot of challenges and get to the edges of your Scala type system skills and learn quite a bit about type inference order, implicit parameters, etc.

cvogt
  • 11,260
  • 30
  • 46
  • How can I get ride of `MySQLDriver`, so that I can apply my code to different driver types ? – jilen Mar 27 '14 at 01:41
  • You can find loads of examples on the net. One in our examples project: https://github.com/slick/slick-examples/blob/master/src/main/scala/com/typesafe/slick/examples/lifted/MultiDBExample.scala – cvogt Mar 27 '14 at 07:42
  • 1
    This seems not work for Option types. How can I define I method works with Option[xxx] type? – jilen Apr 24 '14 at 01:38
  • Does using TypedType help? Otherwise you could have a look into OptionMappers used here https://github.com/slick/slick/blob/2.0.1/src/main/scala/scala/slick/lifted/ExtensionMethods.scala – cvogt Apr 24 '14 at 08:33
  • `TypedType` not work, and I really don't know how to use `OptionMappers` – jilen Apr 24 '14 at 10:51