alexarchambault / scalacheck-shapeless Goto Github PK
View Code? Open in Web Editor NEWGeneration of arbitrary case classes / ADTs instances with scalacheck and shapeless
License: Apache License 2.0
Generation of arbitrary case classes / ADTs instances with scalacheck and shapeless
License: Apache License 2.0
Maybe not a ScalaCheck-Shapeless bug per se, but after switching from Scala 2.12.3 with -Xlint
enabled I began getting unused value warnings for implicit Arbitrary
instances for (Shapeless) tagged types in scope.
To illustrate:
// build.sbt
name := "sample"
version := "0.1"
scalaVersion := "2.12.3"
scalacOptions :=
"-Xfatal-warnings" ::
"-Xlint" ::
Nil
libraryDependencies ++=
"com.chuusai" %% "shapeless" % "2.3.3" ::
"com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.8" % Test ::
Nil
// src/test/scala/Unused.scala
import org.scalacheck.Arbitrary._
import org.scalacheck.ScalacheckShapeless._
import org.scalacheck._
import shapeless.tag
import shapeless.tag._
object Unused {
sealed trait Foo
case class Bear(s: String, i: Int @@ Foo)
val gen: Gen[Bear] = {
implicit val arbFoo: Arbitrary[Int @@ Foo] = Arbitrary(arbitrary[Int].map(tag[Foo].apply[Int]))
arbitrary[Bear]
}
}
That works fine with 2.12.3
, but assigning 2.12.4
to scalaVersion
and recompiling results in:
sbt:sample> ;clean;test:compile
[success] Total time: 2 s, completed Jan 28, 2018, 1:44:20 PM
sbt:sample> set scalaVersion := "2.12.4"
sbt:sample> ;clean;test:compile
[error] /tmp/sample/src/test/scala/Unused.scala:13:16: local val arbFoo in value gen is never used
[error] implicit val arbFoo: Arbitrary[Int @@ Foo] = Arbitrary(arbitrary[Int].map(tag[Foo].apply[Int]))
[error] ^
Setting -Xlint:-unused,_
gets this compiling with 2.12.4.
Can support for arbitrary tagged types be added to ScalaCheck-Shapeless?
Scala Native 0.4.0 is now released. The following project is using this library.
It might be a good time to drop Scala.js 0.6.x in order to update all the dependencies.
I'm having trouble trying to generate samples from nested case classes containing sealed traits.
When I try to generate an instance of a nested case class that contains sealed traits, the generator often fails, returning None
.
Gen is supposed to fail when filtering is wrong. In my experiments Gen is failing even though I'm not applying any filter (also, it looks like gen.retryUntil(_ => true)
is ignored and None
is often returned).
I was not able to isolate the problem. Here is my experiment :)
package object models {
case class C(e: String, f: OffsetDateTime, g: OffsetDateTime, c: Option[String], big: BigInt)
case class B(e: String, f: OffsetDateTime, g: OffsetDateTime, outcome: Planet, outcome2: Planet, c: C)
case class A(a: String, b: String, c: Option[String], d: B, outcome: Planet, outcome2: Planet)
sealed abstract trait Planet
object Planet {
case object Earth extends Planet
case object Sun extends Planet
case object Moon extends Planet
}
}
object Test extends App {
import org.scalacheck.Shapeless._
import org.scalacheck._
import models._
implicit val offsetDateTimeArbitrary: Arbitrary[OffsetDateTime] = Arbitrary(Gen.const(OffsetDateTime.now()))
val gen = implicitly[Arbitrary[A]].arbitrary
val n = 1000
println(Seq.fill(n)(gen.retryUntil(_ => true).sample).flatten.length == n)
}
note: I'm using scala 2.11.7
and "scalacheck-shapeless_1.13" % "1.1.0-RC1"
.
Failing generators are really bad now that we have non-constant arbitrary functions in ScalaCheck 1.13. Here's an example of a place this comes up in this library (derived from an example by @nrinaudo):
import org.scalacheck._, Shapeless._
sealed trait Foo; case object Bar extends Foo
val prop = Prop.forAll { (f: Int => Foo) => f(0); true }
And:
scala> prop.check
! Exception raised on property evaluation.
> ARG_0: <function1>
> Exception: org.scalacheck.Gen$RetrievalError: couldn't generate value
org.scalacheck.Gen.loop$1(Gen.scala:57)
org.scalacheck.Gen.doPureApply(Gen.scala:58)
...
I've taken a shot at a diagnosis here, but in short it looks like something like this in MkCoproductArbitrary.ccons
would work:
Arbitrary {
Gen.sized {
case size =>
val sig = math.signum(size)
try {
Gen.frequency(
1 -> Gen.resize(size - sig, Gen.lzy(headArbitrary.value.arbitrary)).map(Inl(_)),
n() -> Gen.resize(size - sig, Gen.lzy(tailArbitrary.arbitrary.arbitrary)).map(Inr(_))
)
} catch {
case _: StackOverflowError => Gen.fail
}
}
}
(I'd also just use math.min(0, size - 1)
instead of the signum
stuff, but that's not really relevant.)
Catching the stack overflow is an awful hack, but it avoids the Gen.fail
on size == 0
for non-recursive ADTs while still not stack-overflowing for recursive ones.
I have a case class with a lot of members that yields well to generating an Arbitrary
instance using scalacheck-shapeless, but some individual field values I'd like to discard as invalid.
For that purpose, I wanted to generate the instance using this library, call filter
on it and make the result of that implicit
.
I couldn't find anything in the documentation explaining how to generate an instance without making it implicit.
After some trial and error, I came up with:
implicit val arbitraryFoo: Arbitrary[Foo] = {
val baseArbitrary = implicitly[Strict[MkArbitrary[Foo]]].value.arbitrary.arbitrary
baseArbitrary.filter(validationCheck)
}
Is there a better way I'm missing?
If yes, it should be added to the documentation.
If not, this seems useful and can be implemented (see semiauto
generic derivation in Circe as an example).
It's already that way in master, but there's no published release which supports ScalaCheck 1.14.
I wonder if there's a way to set seed for all the random values.
I've had the following issue on shapeless-contrib
, which I think also affects scalacheck-shapeless
: typelevel/shapeless-contrib#33 (comment)
I'm getting a weird behavior in scalacheck while generating
Map[Int, List[Int]]
with an automatically derivedArbitrary
.property("map") = { forAll(implicitly[Arbitrary[Map[Int, List[Int]]]].arbitrary)(ok) }Leads to a:
[info] ! Map.map: Gave up after only 62 passed tests. 312 tests were discarded.Any idea of what's going on here? My quick work around is to generate a
Map[A, B]
from aList[(A, B)]
(I also have a generalized version to build anything with aCanBuildFrom
from aList
):implicit def arbitraryMap[A, B] (implicit a: Arbitrary[List[(A, B)]]): Arbitrary[Map[A, B]] = Arbitrary(a.arbitrary.map(_.toMap))
I'm using
I expect implicitly[Arbitrary[IntWr]]
to use arb
in the code below, but it doesn't.
case class IntWr(int: Int)
val intGen = Gen.choose(5, 9)
def shapelessCustom = {
import org.scalacheck.ScalacheckShapeless._
implicit val arb: Arbitrary[Int] = Arbitrary(intGen)
mkList(implicitly[Arbitrary[IntWr]]) // List(IntWr(-2147483648), IntWr(1263246479), IntWr(1791721042), IntWr(0), IntWr(-90407902))
}
private def mkList[T](gen: Arbitrary[T]): List[T] =
List.fill(10)(gen.arbitrary.sample.get)
For instance, here the correct Arbitrary[Int] is used:
def custom: List[Int] = {
implicit val arb: Arbitrary[Int] = Arbitrary(intGen)
mkList(implicitly[Arbitrary[Int]]) // List(7, 5, 5, 8, 6, 8, 7, 5, 7, 6)
}
Is this behavior expected? Should it be considered a bug?
please please please
I'm curious how this library differences from shapeless-scalacheck
. Maybe you can add a note on the difference for those who already use that?
Like those of milessabin/shapeless#277
Possible workaround:
https://github.com/notxcain/si-7046-workaround
Assuming that I have foo from the docs:
case class Foo(i: Int, s: String, blah: Boolean)
I want to define:
object FooGenerators {
implicit val arbFoo: Arbitrary[Foo] = ???
}
This doesn't work since it finds itself:
implicit val arbFoo: Arbitrary[Foo] = implicitly[Arbitrary[Foo]]
This also doesn't seem to work:
implicit val arbFoo: Arbitrary[Foo] = derivedArbitrary[Foo]
It fails with:
Error:(11, 41) could not find implicit value for parameter ev: shapeless.LowPriority
implicit val arbFoo: Arbitrary[Foo] = derivedArbitrary
Error:(11, 41) not enough arguments for method derivedArbitrary: (implicit ev: shapeless.LowPriority, implicit underlying: shapeless.Strict[org.scalacheck.derive.MkArbitrary[T]])org.scalacheck.Arbitrary[T].
Unspecified value parameters ev, underlying.
implicit val arbFoo: Arbitrary[Foo] = derivedArbitrary
I have to end up doing something like:
object FooDerivedGenerators {
val arbFoo: Arbitrary[Foo] = implicitly[Arbitrary[Foo]]
}
object FooGenerators {
implicit val arbFoo: Arbitrary[Foo] = FooDerivedGenerators.arbFoo
}
https://github.com/adelnizamutdinov/shapeless-rc6-scalacheck Here's a sample project that compiles successfully with 2.2.0-RC4
but when I change that to 2.2.0-RC6, compilation goes on forever (though it sometimes finishes saying that it could not find an implicit Arbitrary)
I'm using https://github.com/fthomas/refined and I was pretty sure it had been working fine but today I got a ClassCastException
when shrinking a case class that contained a field which was a Refined
type. My guess is it has something to do with it being a value class.
Here is the exception:
Caused by: java.lang.ClassCastException: java.lang.Integer cannot be cast to eu.timepit.refined.api.Refined
at com.geneious.nucleus.service.job.api.generators.JobStatusGenerators$anon$macro$381$1.from(JobStatusGenerators.scala:20)
at com.geneious.nucleus.service.job.api.generators.JobStatusGenerators$anon$macro$381$1.from(JobStatusGenerators.scala:20)
at org.scalacheck.derive.MkShrink$.$anonfun$genericProduct$2(MkShrink.scala:41)
at scala.collection.immutable.Stream.map(Stream.scala:415)
at org.scalacheck.derive.MkShrink$.$anonfun$lazyxmap$1(MkShrink.scala:32)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$4(MkShrink.scala:89)
at scala.collection.immutable.Stream.append(Stream.scala:252)
at scala.collection.immutable.Stream$ConsWrapper.$hash$colon$colon$colon(Stream.scala:1126)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$4(MkShrink.scala:89)
at scala.collection.immutable.Stream.append(Stream.scala:252)
at scala.collection.immutable.Stream$ConsWrapper.$hash$colon$colon$colon(Stream.scala:1126)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$4(MkShrink.scala:89)
at scala.collection.immutable.Stream.append(Stream.scala:252)
at scala.collection.immutable.Stream$ConsWrapper.$hash$colon$colon$colon(Stream.scala:1126)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkShrink$.$anonfun$lazyxmap$1(MkShrink.scala:32)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.Shrink$.shrink(Shrink.scala:44)
at org.scalacheck.Prop$.$anonfun$forAll$16(Prop.scala:913)
at org.scalacheck.Prop$.shrinker$1(Prop.scala:778)
at org.scalacheck.Prop$.$anonfun$forAllShrink$1(Prop.scala:802)
at org.scalacheck.Prop$.$anonfun$apply$1(Prop.scala:307)
at org.scalacheck.PropFromFun.apply(Prop.scala:22)
at org.scalacheck.Prop$.result$1(Prop.scala:762)
at org.scalacheck.Prop$.$anonfun$forAllShrink$1(Prop.scala:800)
at org.scalacheck.Prop$.$anonfun$apply$1(Prop.scala:307)
at org.scalacheck.PropFromFun.apply(Prop.scala:22)
at org.scalacheck.Test$.workerFun$1(Test.scala:326)
at org.scalacheck.Test$.$anonfun$check$1(Test.scala:355)
at org.scalacheck.Test$.$anonfun$check$1$adapted(Test.scala:355)
at org.scalacheck.Platform$.runWorkers(Platform.scala:40)
at org.scalacheck.Test$.check(Test.scala:355)
at org.scalatest.enablers.UnitCheckerAsserting$CheckerAssertingImpl.check(CheckerAsserting.scala:89)
... 53 more
Here is the relevant code:
sealed trait JobStatus {
def kind: String
def dateTime: OffsetDateTime
def messages: immutable.Seq[String]
def progress: Percentage
}
final case class Running(dateTime: OffsetDateTime,
messages: immutable.Seq[String],
progress: Percentage) extends JobStatus {
@transient
override val kind: String = Running.Kind
}
object CommonRefinedTypes {
type Percentage = Int Refined Interval.Closed[W.`0`.T, W.`100`.T]
object Percentage extends RefinedTypeOps.Numeric[Percentage, Int]
}
object CommonRefinedTypeGenerators {
implicit val arbPercentage: Arbitrary[Percentage] = numeric.intervalClosedArbitrary
implicit val shrinkPercentage: Shrink[Percentage] = shrinkFrom(Percentage)
private def shrinkFrom[A <: Refined[B, _], B: Shrink](ops: RefinedTypeOps[A, B]): Shrink[A] = Shrink { a =>
shrink(a.value).flatMap { shrunk =>
ops.from(shrunk).fold(_ => Stream.empty, Stream(_))
}
}
}
import com.geneious.nucleus.service.util.generators.CommonRefinedTypeGenerators._
trait JobStatusGenerators {
implicit val arbRunningJobStatus: Arbitrary[Running] = MkArbitrary[Running].arbitrary
implicit val shrinkRunningJobStatus: Shrink[Running] = MkShrink[Running].shrink
}
I looked on your profile for a public e-mail or means of contacting you, but I could find none. I just wanted to let you know that this is the first Shapeless-based project that I've been able to understand just by reading its source. You have done a very good job in designing your codebase. I hope you continue to develop this project. ๐
I noticed that ArbitraryTests has a test for Arbitrary[T1.Tree], but there's no similar test in CogenTests. I tried to add one at CogenTests.scala:146, but the expression Cogen[T1.Tree] crashes:
java.lang.StackOverflowError
at shapeless.Lazy$$anon$1.value$lzycompute(lazy.scala:121)
at shapeless.Lazy$$anon$1.value(lazy.scala:121)
at org.scalacheck.derive.DerivedInstances$class.derivedCogen(Instances.scala:122)
at org.scalacheck.Shapeless$.derivedCogen(Shapeless.scala:5)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$313$lzycompute(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$313(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$306$lzycompute(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$306(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1$$anonfun$inst$macro$304$1.apply(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1$$anonfun$inst$macro$304$1.apply(CogenTests.scala:146)
at shapeless.Lazy$$anon$1.value$lzycompute(lazy.scala:121)
at shapeless.Lazy$$anon$1.value(lazy.scala:121)
at org.scalacheck.derive.MkCogen$$anonfun$genericCoproduct$1.apply(MkCogen.scala:42)
at org.scalacheck.derive.MkCogen$$anonfun$genericCoproduct$1.apply(MkCogen.scala:42)
at org.scalacheck.derive.MkCogen$$anon$1.cogen(MkCogen.scala:27)
at org.scalacheck.derive.DerivedInstances$class.derivedCogen(Instances.scala:122)
at org.scalacheck.Shapeless$.derivedCogen(Shapeless.scala:5)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$321$lzycompute(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$321(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$320$lzycompute(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1.inst$macro$320(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1$$anonfun$inst$macro$315$1.apply(CogenTests.scala:146)
at org.scalacheck.CogenTests$$anonfun$13$$anonfun$apply$14$anon$derivedCogen$macro$330$1$$anonfun$inst$macro$315$1.apply(CogenTests.scala:146)
at shapeless.Lazy$$anon$1.value$lzycompute(lazy.scala:121)
at shapeless.Lazy$$anon$1.value(lazy.scala:121)
[... and so on]
Reported by @raulraja on the gitter of shapeless (https://gitter.im/milessabin/shapeless?at=56fcf50fbbffcc665faad6e5)
import org.scalacheck.Shapeless._
import shapeless._
import org.scalacheck._
def test[L <: HList : Arbitrary](l : L) : Arbitrary[L] = implicitly[Arbitrary[L]]
test(1 :: HNil) // works
import shapeless.syntax.singleton._
test(1.narrow :: HNil)
Surprisingly, explicitly specifying the type parameter of test
makes this work again,
test[Witness.`1`.T :: HNil](1.narrow :: HNil) // fine
In particular,
Are there plans to update this library to Scala 3?
example of it happening on Ubuntu, in the Scala community build: https://scala-ci.typesafe.com/job/scala-2.13.x-jdk14-integrate-community-build/628/console
but I have also seen it happen outside dbuild, on MacOS + JDK 14 (14-ea+32-1423) + Scala 2.13.1. just by cloning the repo and then doing testJVM/test
. here's the crash log: https://gist.github.com/SethTisue/849ae992455477669d595e3e6510f1a0
E.g. given:
object WeekDay extends Enumeration {
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
it should be possible to derive Arbitrary[WeekDay.Value]
.
The implementation is a one-liner:
implicit def arbEnum[E <: Enumeration](implicit enum: Witness.Aux[E]): Arbitrary[E#Value] =
Arbitrary(Gen.oneOf(enum.value.values.toSeq))
Happy to make a PR if this fits in the library.
I have sealed trait similar to this one:
sealed trait Expression
object Expression {
case class Identifier(name: String) extends Expression
case class Num(x: Double) extends Expression
case class Add(x: Expression, y: Expression) extends Expression
}
Now, I'd like to exhaustively generate an Arbitrary[Expression]
with scalacheck-shapeless
, which uses my custom implementation of Arbitrary[Expression.Identifier]
. Is that possible somehow?
My problem is that I have certain requirements on the name: String
field in Identifier
, which throws away too many test cases when I just filter the Arbitrary[Expression]
Either have a more structured README.md, or a generated one (scalatex?)
Hi, thanks for the great library.
I am using scalacheck-shapeless to generate instances of an ADT (sealed trait + case classes/objects). But when a property fails and scalacheck tries to shrink the input, the shrinking continues forever.
Minimal reproduction:
sealed trait Foo
case object A extends Foo
case object B extends Foo
object Test extends Properties("whatevs") {
property("failing prop without shrinking") = forAllNoShrink { (x: Foo) =>
false
}
property("failing prop with shrinking") = forAll { (x: Foo) =>
println(x)
false
}
}
The forAllNoShrink
props fails as expected, but the forAll
one never completes. I can see from the println
that the original input is A
, which is then shrunk to B
, which is then shrunk to A
, ... ad infinitum.
The reproduction is available as a complete sbt project here.
In particular,
Arbitrary
and Shrink
),I'm trying to generate an Arbitrary
for a pretty long class which has many types, the class is itself generated through a macro (using a library called SCIO Beam).
For a simple generated class I'm able to use scalacheck-shapeless to generate an Arbitrary instance, but for a bigger class I'm not able to. How can I debug why generating the implicit failed? I imagine one cause could be a specific type for which no Arbitrary was found?
To illustrate, this is working:
@BigQueryType.fromStorage(
"<redacted>"
)
class Test
// Generates a class with two columns, one of type String and one of type Long
it should "work" in {
implicitly[Arbitrary[Test]]
// It was able to create the implicit!
val genTest: Gen[Test] = Arbitrary.arbitrary[Test]
...
}
But for another class generated from a BigQuery table with many columns, it's not finding the implicit.
Do I have a way to explicitly invoke what generates the implicit method that we fetch with implicitly[Arbitrary[Test]]
? And grab the potential Failure?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.