0

I have a basic Table / Column DSL definition.

import scala.collection.parallel.mutable.ParHashSet

abstract class Column[Owner <: Table[Owner, Record], Record, ValueType](table: Table[Owner, Record]) {
   table.addColumn(this)
}

class Table[Owner <: Table[Owner, Record], Record] {
   private[this] val _columns = ParHashSet.empty[Column[Owner, Record, _]]

   def columns: List[Column[Owner, Record, _]] = _columns.toList
   def addColumn(column: Column[Owner, Record, _]) {
     _columns += column
   }
}
class SpecialColumn[Owner <: Table[Owner, Record], Record, ValueType](table: Table[Owner, Record]) extends Column[Owner, Record, ValueType](table) {//blabla}

The point is to define a Table like this:

case class SomeModel(name: String, prop: String)
sealed class SomeTable extends Table[SomeTable, SomeModel] {
   object name extends Column[SomeTable, SomeModel, String](this)
   object prop extends SpecialColumn[SomeTable, SomeModel, String](this)
}
object SomeTable extends SomeTable {}

I want SomeTable to store references to the objects defined inside it. Therefore the Column constructor calls addColumn on instantiation.

Therefore SomeTable.columns.length should be 2, but it's always 1.

What am I missing here? Here's a Scala fiddle.

3
  • You mean it's always 0? Commented Jan 26, 2014 at 1:13
  • Mhhhh, aside from reflection I don't see many hooks. I remember Lift Web Framework defining something like this in the Mapper module, if I remember correctly. Commented Jan 26, 2014 at 1:19
  • Well, but in Mapper you define the fields, can't you go away with that? Do you want to just define the columns and having all the fields definition goodies? Commented Jan 26, 2014 at 1:22

2 Answers 2

0

object name and object prop are declarations. Their constructors won't get called until they are referenced.

Try changing to

  class SomeTable extends Table[SomeTable, SomeModel] {
    object name extends Column[SomeTable, SomeModel, String](this)
    object prop extends SpecialColumn[SomeTable, SomeModel, String](this)
    val n = name
    val p = prop
  }

to see how that changes the output of your test.

My preference would actually be

class SomeTable extends Table[SomeTable, SomeModel] {
  val name = new Column[SomeTable, SomeModel, String](this) {}
  val prop = new SpecialColumn[SomeTable, SomeModel, String](this)
}

to avoid the redundancy of having both the declaration and the reference, and to make the initialization of the columns deterministic.

Non-deterministic initialization of objects in Scala is something you always have to watch out for, as this question illustrates.

Sign up to request clarification or add additional context in comments.

6 Comments

There is no need to keep/declare vals, you can just reference object names.
Right, that's just an example of how to reference the object names. You could reference them somewhere else.
Perhaps you meant "Their constructors" instead of "The constructor". SomeTable constructor will obviously get called in this example very early.
Actually my preference would be val name = new Column.... It avoids the redundancy of having both the declaration and the reference.
object name ...; val n = name here is almost identical to val name = new .... You will be able to override val n and val p but you will leave object name and object prop there unreferenced in that case which breaks implementation.
|
0

This is an effect of lazy evaluation. Objects on JVM get created lazily and classes get loaded lazily as needed - when being used/referenced first time.

println(s"Number of columns: ${SomeTable.columns.length}")
// prints 0
SomeTable.name
SomeTable.prop
println(s"Number of columns: ${SomeTable.columns.length}")
// prints 2

So just by referencing the fields you get them initialized in the example above.

If you change your code from:

class SomeTable extends Table[SomeTable, SomeModel] {
  object name extends Column[SomeTable, SomeModel, String](this)
  object prop extends SpecialColumn[SomeTable, SomeModel, String](this)
}

to:

class SomeTable extends Table[SomeTable, SomeModel] {
  val name = new Column[SomeTable, SomeModel, String](this){}
  val prop = new SpecialColumn[SomeTable, SomeModel, String](this)
}

you will get 2 printed consistently as you desired.

The reason it works is that in the first case with object fields Scala would create those objects when you reference them, but in the second case you are initializing variables, so the values have to be computed. Notice that you can't override an object field in a subclass, but you can override val or def - so there are some "small?" differences.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.