typelevel / scalacheck

Property-based testing for Scala
http://www.scalacheck.org
BSD 3-Clause "New" or "Revised" License
1.94k stars 404 forks source link

Gen.sequence seems to forget that ScalaCheck is a Scala lib (1.12.1) #131

Open devmage opened 9 years ago

devmage commented 9 years ago

Consider the following console session, with ScalaCheck 1.12.1:

scala> import org.scalacheck.Gen
import org.scalacheck.Gen

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

scala> val genInts = Gen.nonEmptyListOf(arbitrary[Int])
genInts: org.scalacheck.Gen[List[Int]] = org.scalacheck.Gen$$anon$2@393c86dd

scala> case class Bar(str: String, i: Int)
defined class Bar

scala> def genBarWith(i: Int) = for { str <- arbitrary[String] } yield Bar(str, i)
genBarWith: (i: Int)org.scalacheck.Gen[Bar]

scala> val genBarList = for { ints <- genInts; bars <- Gen.sequence { ints.map(genBarWith) } } yield bars
genBarList: org.scalacheck.Gen[java.util.ArrayList[Bar]] = org.scalacheck.Gen$$anon$6@2d97c1a

scala> val genBarList2 = for { ints <- genInts; bars <- Gen.sequence[List[Bar], Bar] { ints.map(genBarWith) } } yield bars
genBarList2: org.scalacheck.Gen[List[Bar]] = org.scalacheck.Gen$$anon$6@57b19137

Incidentally, the type parameters on genBarList2 cause IntelliJ to refuse to compile, although sbt and the REPL are fine with it.

To get back to Scala-land, I have to import scala.collection.JavaConverters._ and map on the generator with .map(_.asScala.toList).

Happy New Year! :smiley:

douglaz commented 9 years ago

Stumbled on this. It's very annoying.

jedws commented 9 years ago

It actually is very aware that it is Scala, and that Scala is not very good at type-inference. For some reason Scala infers the type j.u.List.

As usual in Scala, some judicious use of explicit type parameters solves the day. The first parameter is the fully specified container, and the second is the element type:

scala> val genBarList = for { ints <- genInts; bars <- Gen.sequence[List[Bar], Bar] { ints.map(genBarWith) } } yield bars genBarList: org.scalacheck.Gen[List[Bar]] = org.scalacheck.Gen$$anon$6@36ffdb7f

cheers, jed.

On 26 May 2015 at 07:32, Allan Douglas R. de Oliveira < notifications@github.com> wrote:

Stumbled on this. It's very annoying.

— Reply to this email directly or view it on GitHub https://github.com/rickynils/scalacheck/issues/131#issuecomment-105321385 .

devmage commented 9 years ago

@jedws in my example above, I offer exactly your proposed solution in genBarList2, so thank you for confirming the behavior on your side. :)

The point here, however, is that without explicit type parameters the resultant List[A] is a java.util.ArrayList[A] instead of a scala.collection.immutable.List[A]. It figures out the A just fine; I'm accustomed to this being a problem, rather than the collection type.

One would think that a Scala library method would try to infer first to Scala types, e.g. here Scala collections. Instead, we see that Gen.sequence seems to infer first to a Java collection. Small annoyance, but it exists.

jedws commented 9 years ago

@devmage sorry, didn't see your solution

Unfortunately, Scala's type inference is not very good. It is often necessary to explicitly annotate in places where it seems silly and that the compiler should do a better job of it.

Could scalacheck have a better signature, or provide a better default Buildable? It probably can! That would be a great PR.