Łukasz
Łukasz

Reputation: 8663

Why columns in slick Tables are defs instead of vals?

In slick documentation columns in table are defined with def

class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
  def name = column[String]("COF_NAME", O.PrimaryKey)
  def supID = column[Int]("SUP_ID")
  def price = column[Double]("PRICE")
  def sales = column[Int]("SALES", O.Default(0))
  def total = column[Int]("TOTAL", O.Default(0))
  def * = (name, supID, price, sales, total)
}

Is there a reason why it shouldn't be like this:

class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
  val name = column[String]("COF_NAME", O.PrimaryKey)
  val supID = column[Int]("SUP_ID")
  val price = column[Double]("PRICE")
  val sales = column[Int]("SALES", O.Default(0))
  val total = column[Int]("TOTAL", O.Default(0))
  val * = (name, supID, price, sales, total)
}

It seems like columns don't use anything that could change.

Upvotes: 2

Views: 235

Answers (1)

Paul Dolega
Paul Dolega

Reputation: 2476

Short answer: initialization order.

Long answer:

Lets gather some facts:

1) using vals instead of defs mostly works (if you change most of defs to vals in your code it would most of the time behave correctly - at least this is how it works for me)

2) most examples of Slick code use defs.

So it may be that if something wrong happens it happens only in some (rare?) cases.

Some light may be shed by this part of code in Slick itself (take a look at comment after if(tt == null) (class `RelationalProfile):

def column[C](n: String, options: ColumnOption[C]*)(implicit tt: TypedType[C]): Rep[C] = {
      if(tt == null) throw new NullPointerException(
        "implicit TypedType[C] for column[C] is null. "+
        "This may be an initialization order problem. "+
        "When using a MappedColumnType, you may want to change it from a val to a lazy val or def.")
      new Rep.TypedRep[C] {
        override def toNode =
          Select((tableTag match {
            case r: RefTag => r.path
            case _ => tableNode
          }), FieldSymbol(n)(options, tt)) :@ tt
        override def toString = (tableTag match {
          case r: RefTag => "(" + _tableName + " " + r.path + ")"
          case _ => _tableName
        }) + "." + n
      }
    }

If you take a look at commit that introduced this change: https://github.com/slick/slick/commit/be2ff6513d46abc9a25c8752c2931a786d4c5ad6

you should find pretty good explanation (commit comments).

Upvotes: 3

Related Questions