Adding Multiple Implicit `Arbitrary[Foo]` without Collisions?

109 views
Skip to first unread message

Kevin Meredith

unread,
Apr 20, 2015, 3:18:22 PM4/20/15
to scala...@googlegroups.com

I wrote a ScalaCheck property:

Imports

scala> import org.scalacheck.{Properties, Arbitrary, Gen}
import org.scalacheck.{Properties, Arbitrary, Gen}

scala> import org.scalacheck.Prop.forAll
import org.scalacheck.Prop.forAll

scala> import org.scalacheck.Prop._
import org.scalacheck.Prop._

scala> import org.scalacheck.Arbitrary._
import org.scalacheck.Arbitrary._

Making a Gen and Arbitrary for Foo

scala> case class Foo(x: Any)
defined class Foo

scala> val fooGen: Gen[Foo] = arbitrary[Int].map(Foo(_))
fooGen: org.scalacheck.Gen[Foo] = org.scalacheck.Gen$$anon$6@74da2066

scala> implicit val arbitraryFoo: Arbitrary[Foo] = Arbitrary(fooGen)
arbitraryFoo: org.scalacheck.Arbitrary[Foo] = org.scalacheck.Arbitrary$$anon$3@6f4c397e

scala> forAll { _: Foo => (true == true) }
res2: org.scalacheck.Prop = Prop

However, I'd like to specify that this property holds for a Double too.

So I added another Gen[Foo] and Arbitrary[Foo]:

scala> val fooGenDouble: Gen[Foo] = arbitrary[Double].map(Foo(_))
fooGenDouble: org.scalacheck.Gen[Foo] = org.scalacheck.Gen$$anon$6@31d5eb6c

scala> implicit val arbitraryFooDouble: Arbitrary[Foo] = Arbitrary(fooGenDouble)
arbitraryFooDouble: org.scalacheck.Arbitrary[Foo] = org.scalacheck.Arbitrary$$anon$3@6a43082d

But, when I try to define another property, I get an ambiguous implicit:

scala> forAll { _: Foo => (true == true) }
<console>:22: error: ambiguous implicit values:
 both value arbitraryFoo of type => org.scalacheck.Arbitrary[Foo]
 and value arbitraryFooDouble of type => org.scalacheck.Arbitrary[Foo]
 match expected type org.scalacheck.Arbitrary[Foo]
              forAll { _: Foo => (true == true) }
                     ^

How is this typically handled in a Scalacheck test?

Note - I also posted this question in StackOverflow - http://stackoverflow.com/questions/29755843/implicit-collision-with-scalacheck.

Rickard Nilsson

unread,
Apr 20, 2015, 3:45:19 PM4/20/15
to scala...@googlegroups.com
Hi Kevin,

On 04/20/2015 09:01 PM, Kevin Meredith wrote:
> I wrote a ScalaCheck property:
>
> Imports
>
> |scala> import org.scalacheck.{Properties, Arbitrary, Gen}
> import org.scalacheck.{Properties, Arbitrary, Gen}
>
> scala> import org.scalacheck.Prop.forAll
> import org.scalacheck.Prop.forAll
>
> scala> import org.scalacheck.Prop._
> import org.scalacheck.Prop._
>
> scala> import org.scalacheck.Arbitrary._
> import org.scalacheck.Arbitrary._|
>
> Making a Gen and Arbitrary for |Foo|
>
> |scala> case class Foo(x: Any)
> definedclass Foo
>
> scala> val fooGen: Gen[Foo] = arbitrary[Int].map(Foo(_))
> fooGen: org.scalacheck.Gen[Foo] = org.scalacheck.Gen$$anon$6@74da2066
>
> scala> implicit val arbitraryFoo: Arbitrary[Foo] = Arbitrary(fooGen)
> arbitraryFoo: org.scalacheck.Arbitrary[Foo] = org.scalacheck.Arbitrary$$anon$3@6f4c397e
>
> scala> forAll{ _: Foo => (true == true) }
> res2: org.scalacheck.Prop = Prop|
>
> However, I'd like to specify that this property holds for a |Double| too.
>
> So I added another |Gen[Foo]| and |Arbitrary[Foo]|:
>
> |scala> val fooGenDouble: Gen[Foo] = arbitrary[Double].map(Foo(_))
> fooGenDouble: org.scalacheck.Gen[Foo] = org.scalacheck.Gen$$anon$6@31d5eb6c
>
> scala> implicit val arbitraryFooDouble: Arbitrary[Foo] = Arbitrary(fooGenDouble)
> arbitraryFooDouble: org.scalacheck.Arbitrary[Foo] = org.scalacheck.Arbitrary$$anon$3@6a43082d|
>
> But, when I try to define another property, I get an ambiguous implicit:
>
> |scala> forAll{ _: Foo => (true == true) }
> <console>:22: error: ambiguousimplicit values:
> both value arbitraryFoo oftype => org.scalacheck.Arbitrary[Foo]
> and value arbitraryFooDouble oftype => org.scalacheck.Arbitrary[Foo]
> match expectedtype org.scalacheck.Arbitrary[Foo]
> forAll{ _: Foo => (true == true) }
> ^|
>
> How is this typically handled in a Scalacheck test?

First of all, in the general case you can always skip using Arbitrary
and simply specify your generator explicitly like this:

forAll(fooGenDouble) { foo => ... }

I actually recommend using this approach as soon as you start dealing
with more than a single generator for your type. Multiple Arbitrary
instances for the same type doesn't really make sense, and using an
explicit generator in a forAll call actually makes it clearer exactly
what the property specifies. This might be a matter of taste, though.

In your specific case, I'm actually not sure what you want to achieve,
having two properties for different actual types of `x`. Since Foo.x
will always be of type Any (if you don't cast it), neither the property
or the "real" code using Foo can behave differently if x is Int or Double.

If you would add a type parameter to your class, it would at least be
possible to define two Arbitrary instances for it, like this:

case class Foo[T](x: T)

implicit val arbitraryFooInt: Arbitrary[Foo[Int]]
implicit val arbitraryFooDouble: Arbitrary[Foo[Double]]

And you could write specific properties that could specify different
things depending on the actual type:

forAll(genFooInt) { foo => ... } // foo.x : Int
forAll(genFooDouble) { foo => ... } // foo.x : Double


/ Rickard

>
> Note - I also posted this question in StackOverflow
> - http://stackoverflow.com/questions/29755843/implicit-collision-with-scalacheck.
>
> --
> You received this message because you are subscribed to the Google
> Groups "scalacheck" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to scalacheck+...@googlegroups.com
> <mailto:scalacheck+...@googlegroups.com>.
> To post to this group, send email to scala...@googlegroups.com
> <mailto:scala...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/scalacheck.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages