1

Hey guys, im working on a project in scala and i encountered very weird problem. This is a part of the code :

class Entity(val id:String){
  override def toString = id
}

class RequirementType
case class DependsEither(t:List[(Entity,Int)]) extends RequirementType
case class Depends(t:(Entity,Int)) extends RequirementType

class BuildableEntity(override val id:String,
               val mineralCost:Int,
               val gasCost:Int,
               val buildTime:Int,
               val buildCount:Int,
               val supplyCount:Int,
               val req:List[RequirementType],
               val onBuildStart: GameState => GameState,       
               val onBuildFinish: GameState => GameState
            )extends Entity(id)

class SimpleBuilding(id:String,
       mineralCost:Int,
       gasCost:Int,
       buildTime:Int,
       req:List[RequirementType]
) extends BuildableEntity(id,mineralCost,gasCost,buildTime,1,0,req:::List(ConsumesOnStart((Drone,1))),{s=>s},{x=>x})
object SpawningPool extends SimpleBuilding("spawningPool",200,0,65,List(DependsEither(List((Hatchery,1),(Lair,1),(Hive,1)))))
object Lair extends SimpleBuilding("lair",150,100,80,List(ConsumesOnFinish(Hatchery,1),Depends(SpawningPool,1)))
object InfestationPit extends SimpleBuilding("infestationPit",100,100,50,List(DependsEither(List((Lair,1),(Hive,1)))))

Now, when i call println(Lair.req), it sometimes prints as

List(ConsumesOnFinish((hatchery,1)), Depends((null,2)), ConsumesOnStart((drone,1)))

and sometimes as

List(ConsumesOnFinish((hatchery,1)), Depends((spawningPool,2)), ConsumesOnStart((drone,1)))

Please, if anyone has any idea about what could be going wrong, i would love you for ever. I have no clue why is it act as it does. I have more extensions of SimpleBuilding but they seem to be working properly

EDIT: I should also mention that the outcome changes after compilation. I mean that when i run unit test it sometimes appear as null and sometimes as proper instance.

Arg
  • 1,926
  • 21
  • 32
  • You haven't posted the code for `Depends` and `SpawningPool` which is where the weirdness happens... – huynhjl Apr 10 '11 at 01:49
  • Stupid me, fixed. Thanks for taking the time and looking into this! :] – Arg Apr 10 '11 at 08:01
  • I am pretty sure it's related to the order of `object` definitions of SpawningPool to Lair. –  Apr 10 '11 at 08:13
  • Well, in my source file the SpawningPool is before lair so that shouldn't be a problem, i think. – Arg Apr 10 '11 at 08:19

3 Answers3

3

Lair use SpawningPool in its constructor and reciprocally. But at that time, the other doesn't exists.

shellholic
  • 5,974
  • 1
  • 20
  • 30
3

You've got recursive definitions in constructors, and although I believe that is supported, it looks like something's going wrong. Can you try lazy vals instead and see if the problem goes away? That is,

object X extends C("this",that,1) { /* code */ }

becomes

lazy val X = new C("this",that,1) { /* code */ }
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
3

This is indeed a case of circular dependency and initialization. Here is a shorter version of your issue:

class X(val x: List[X])
object A extends X(List(B))
object B extends X(List(A))

object Main {
  def main(args:Array[String]) {
    println("A.x: " + A.x)
    println("B.x: " + B.x)
  }
}

This will print this:

$ scala -cp classes Main
A.x: List(B$@143c8b3)
B.x: List(null)

You can use by names parameter to allow object construction to finish before you use it:

class X(x0: => List[X]) {
  lazy val x = x0
}
object A extends X(List(B))
object B extends X(List(A))

The fix works on the small test case:

$ scala -cp classes Main
A.x: List(B$@1feca64)
B.x: List(A$@6d084b)

Based on this you may want to change req:List[RequirementType] to req0: => List[RequirementType] and add a lazy val req = req0.

If that works for you, we should retitle the question to mention object initialization and circular dependencies. Note this is very similar to this question/answer.

Community
  • 1
  • 1
huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • Thanks a lot for the answer. It seems to be working! It's kinda weird that there is no compile time error, which i would have expected. – Arg Apr 11 '11 at 14:18