radumanolescu
radumanolescu

Reputation: 4161

Slick 1.0.0 | Return Expression From For Comprehension

The code below compiles and works fine as shown. However, if I try to yield Some("SomeConstant"), I get the runtime error shown below.

Why is this happening, and how can I return expressions (e.g. Some(...)) from my query?

  def cannotUnpack(db: Database) {
    db.withSession {
      val data = (for {
        rw1 <- TableOne
        rw2 <- TableTwo if rw1.cl1 === rw2.cl1 && rw1.cl2 === rw2.cl2 && rw1.cl1 === "0"
        now = new Timestamp(System.currentTimeMillis())
        six = 6
      } yield (uuid, rw1.cl3, "SomeConstant", six, now) ).list // Works
//    } yield (uuid, rw1.cl3, Some("SomeConstant"), six, now) ).list // Runtime error
    }
  }

Runtime error: Don't know how to unpack (String, scala.slick.lifted.Column[Option[String]], Some[String], scala.slick.lifted.Column[Int], scala.slick.lifted.Column[java.sql.Timestamp]) to T and pack to G
rw2 <- TableTwo if rw1.cl1 === rw2.cl1 && rw1.cl2 === rw2.cl2 && rw1.cl1 === "0"
^

Environment: scala 2.10 on Ubuntu, Java 7 Slick 1.0.0, SQL Server, JTDS driver

Upvotes: 1

Views: 729

Answers (2)

cvogt
cvogt

Reputation: 11270

The short answer: It works, if you write Some("SomeConstant") : Option[String].

The long answer: If you supply a constant as part of your Slick query, Slick has to put the value into the SQL query and later read it back from the results. This preserves composability, i.e. allows you to use the Slick query as a component in another Slick query. In order to encode the value into the SQL query, the method you are calling (in case of a comprehension: map or flatMap) needs to find an implicit value of type TypeMapper[T] where T is the type of the value. Slick does define a TyperMapper[Option[String]] but the problem is that it does not apply in your case, because Some("SomeConstant") is of type Some[String] and there is no TypeMapper[Some[String]] defined in Slick (and TypeMapper[T] is invariant in T). By explicitly supplying :Option[String], you loosen the type information, so a matching TypeMapper can be found.

We will think about if we can add support for constants of type Some into Slick. I added a ticket (https://www.assembla.com/spaces/typesafe-slick/tickets/268) and will bring it up in our next team meeting.

Upvotes: 1

Cristian Boariu
Cristian Boariu

Reputation: 9621

Well, I wouldn't involve constants in select but use them only when managing results loaded from DB.

Try it like:

 def cannotUnpack(db: Database) {
    db.withSession {
      val data = (for {
        rw1 <- TableOne
        rw2 <- TableTwo if rw1.cl1 === rw2.cl1 && rw1.cl2 === rw2.cl2 && rw1.cl1 === "0"
      } yield (uuid, rw1.cl3)
    }
  }

after that get your data ready for what you need:

for (
  (uuid, rw1_cl3) <- data.list
) yield (uuid, rw1_cl3, Some("constant"), 6, new Timestamp(System.currentTimeMillis()))

I usually use an output case class when preparing final data, for instance:

case class Export(uuid: String, rw1: String, constant: Option[String], six: String, now: Timestamp)

for (
      (uuid, rw1_cl3) <- data.list
    ) yield Export(
             uuid, 
             rw1_cl3, 
             Some("constant"),  
             6, 
             new Timestamp(System.currentTimeMillis()))

Upvotes: 0

Related Questions