David Portabella
David Portabella

Reputation: 12720

scalacheck generator produces a null sample

what is wrong? why person is null?

import org.scalacheck.{Arbitrary, Properties, Gen, Prop}
import Gen._
import Prop._

case class Person(name: String) {}

object QuickCheckPerson extends Properties("Person") {
  property("gen1") = forAll { (person: Person) =>
    println("person: " + person)
    person.name == "john"   // nullPointerException, because person == null
  }

  val john = Person("john")
  implicit lazy val arbPerson: Arbitrary[Person] = Arbitrary(value(john))
}

QuickCheckPerson.check

It fails with NullPointerException.

However, the example works if I move the line val john = Person("john") just before property("gen1") = ....

why??

Update

the example works if I declare the val john as lazy. so, it seems that the lazy val arbPerson is executed before the val john, but if so, the scala compilator should fail saying that john is not defined. john is a val, nor a var, so either it is declared and instantiated, or not.

any idea on this?

ps: scala 2.10.3

Upvotes: 0

Views: 1018

Answers (1)

Vladimir Matveev
Vladimir Matveev

Reputation: 127771

so, it seems that the lazy val arbPerson is executed before the val john, but if so, the scala compilator should fail saying that john is not defined. john is a val, nor a var, so either it is declared and instantiated, or not.

No, this is not correct. arbPerson is a method internally, which performs double-checked lock and lazily initializes a private field. Since it is a method, it can be called from anywhere, including constructors. What you have here is essentially the following Java code (of course, it is not valid as it is written here, but it gives the basic idea):

public class QuickCheckPerson extends Properties {

    private final Person john;
    private volatile Arbitrary<Person> arbPerson;

    public QuickCheckPerson() {
        super("Person");  // call superclass constructor
        // property field belongs to superclass
        property.update("gen1", forAll(new Function1<Person, Boolean>() {
                /* closure implementation */ 
            },
            /* implicit parameter converting boolean to Prop */,
            arbPerson(),
            /* other implicits */
        ));
        this.john = Person.apply("john");
    }

    public Arbitrary<Person> arbPerson() {
        // Perform double-checked lock over a bit field (not presented here)
        // and create arbPerson if needed
        // Let's pretend that the following is double-checked lock:
        if (arbPerson == null) {
            arbPerson = Arbitrary.apply(value(john));
        }
        return arbPerson;
    }

}

Nothing in JVM prevents you from calling a method from a constructor, and this is what happens here. arbPerson() method is called before john field is initialized (and by default all reference fields are nulls), so null is supplied to value() method. Hence your NullPointerException.

Please do tell if there are any mistakes in my expansion of Scala code (or edit the post directly), I will fix them.

Upvotes: 1

Related Questions