Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    mrbackend
    @mrbackend

    Well, that might be interesting, but I was thinking of the “problem” that we tend to bind the extra type parameters to Int, and then use Int-specific gens/cogens. Erik shows a solution where those types are picked arbitrarily at runtime as well.

    Come to think of it, it’s not only about GADTs, but type parameters in general.

    Erik mentions some defects they found by varying the type arguments, but my imagination is too limited to think of such cases myself. Except for code with unsafe casts, that is.

    @ashawley
    beezee
    @beezee
    has anyone seen sbt test randomly skip properties before? i'm getting inconsistent output from run to run. everything passes but some properties disappear and reappear from one run to the next
    João Costa
    @JD557
    are you, by chance, using Gen.filter?
    IIRC, if you use that, it's possible that nothing gets generated... I'm not sure if that can cause properties to be skipped
    beezee
    @beezee
    interesting thought. i know i've seen suchThat lead to exhaustion errors before. it was definitely a silent omission, but fwiw at some point it seems to have gotten over whatever was causing the odd behavior and i'm now getting consistent runs of all my properties
    Oliver Schrenk
    @oschrenk

    I’m exploring testing laws of my data structures using cats/kernel/laws/discipline/OrderTests.scala .

    checkAll(“Foo.OrderLaws”, OrderTests[Foo].order)

    Compilation fails with could not find implicit value for parameter arbF: org.scalacheck.Arbitrary[Foo => Foo]. I have a Gen[Foo]. As far as I understand I need also Cogen[Foo]. I don’t quite understand what Cogen is and to construct one. It's not mentioned in the UserGuide.

    Any pointers?

    Georgi Krastev
    @joroKr21
    Cogen takes a value Foo and a seed and produces a new seed that takes Foo into account.
    Usually you can just use contramap, e.g. if you have case class Foo(x: Int, y: String, z: List[Double]) you can just do Cogen[(Int, String, List[Double])].contramap[Foo](Foo.unapply(_).get) (or just spell out foo => (foo.x, foo.y, foo.z))
    Georgi Krastev
    @joroKr21
    Or use any of the other constructors
    Oliver Schrenk
    @oschrenk
    Thanks. I ended up using contramap after looking at implementations on Github, specifically ending up at CogenInstances.scala and it worked.
    I still feel that I don't understand what I am doing. Is there any explanation/documentation of the purpose, reason and meaning behind Cogen? I can't find any. From the name I gather it is part of the generation process but iit sort of stops there. Why does it need a seed or a Long to generate?
    Georgi Krastev
    @joroKr21

    I don't have any links to resources about Cogen, so I wouldn't do a better job than just googling. But you can think of it like this:

    Gen is conceptually a function Seed => (A, Seed) that takes some seed value and produces a random A value based on the seed along with the next seed to use. That is basically how purely functional random number generators work. I learned about it first here: http://learnyouahaskell.com/input-and-output#randomness. Note that this function needs to be deterministic. Given the same seed we must produce the same A and the same next seed. This allows us to reproduce test failures in scalacheck for example. You then easily produce a stream of As given an initial seed by just iteratively feeding the next seed back in.

    Cogen is just the dual of Gen (in category theory Cofoo is just Foo with the direction of arrows reversed). So reversing the direction we get a function (A, Seed) => Seed. This lets us modify a seed to take into account an A. We need this to generate random functions because we want that in A => B, the result B needs to depend on the value of A. But B is random (generated based on a seed). So we need to modify B's seed based on the value of A. And this is exactly what Cogen gives us. So we can then do this: given a seed, generate a random A (via Gen[A]), feed it to a randomly generated function A => B which will internally take the A, generate a new seed and use it to generate a random B. This is all a deterministic process so we preserve the desired property that, given the same initial seed the results will always be the same.

    Mark Tomko
    @mtomko
    I have a Seq[Gen[A]] and I'd like to make a Gen[Seq[A]] which generates a Seq[A] by sampling one item from each Gen[A] in the original Seq. Is there a way to do that?
    I guess that would be a traverse, wouldn't it...
    Mark Tomko
    @mtomko
    Georgi Krastev
    @joroKr21
    When in doubt, traverse :thumbsup:
    Dario Abdulrehman
    @dabd
    Does anyone know why SeededProperties is not part of the library since deterministic generators were introduced in 1.14.0? https://gist.github.com/non/aeef5824b3f681b9cfc141437b16b014
    It is very useful to have seeds always printed out.
    Aaron S. Hawley
    @ashawley
    @dabd Have you tried version 1.14.1?
    Printing the seed was added in typelevel/scalacheck#446
    Dario Abdulrehman
    @dabd
    I'm using Prop.classify and if you have a handful of "classifiers" the nesting is horrible. Could something like this be added to the library?
      @scala.annotation.tailrec
      def classify(conditions: Stream[(Boolean, Any, Any)])(prop: Prop): Prop = conditions match {
        case Stream.Empty => prop
        case h #:: Stream.Empty =>
          if (h._1) collect(h._2)(prop) else collect(h._3)(prop)
        case h #:: t =>
          classify(t)(Prop.classify(h._1, h._2, h._3)(prop))
      }
    Aaron S. Hawley
    @ashawley
    Could you open an issue with an example of nested classifiers?
    I'm not sure I understand your problem.
    Dario Abdulrehman
    @dabd

    @ashawley This is an example from the user guide

    val myProp = forAll { l: List[Int] =>
      classify(ordered(l), "ordered") {
        classify(l.length > 5, "large", "small") {
          l.reverse.reverse == l
        }
      }
    }

    If you have a handful of classifiers the nesting will be a bit ugly.

    Dario Abdulrehman
    @dabd
    Zak Patterson
    @zakpatterson
    I'm having an issue with Gen.posNum not producing values, I just posted #568, doesn't look like this is a known thing. happy to help diagnose tho.
    I
    Zak Patterson
    @zakpatterson
    With the change happening between 1.14.0 and 1.14.1, this should be easy to figure out, i'll work on it.
    Aaron S. Hawley
    @ashawley
    @zakpatterson It's likely the change for typelevel/scalacheck#439
    Zak Patterson
    @zakpatterson
    yes, thank you. Erik just let me know the same on the issue.
    it seems that other parts of scalacheck are not checking this possibility however
    for example:
    scala> Gen.listOfN(10000, Gen.posNum[Int]).sample
    res2: Option[List[Int]] = None
    
    scala> Gen.listOfN(10000, Gen.posNum[Int]).sample
    res3: Option[List[Int]] = None
    
    scala> Gen.listOfN(10000, Gen.posNum[Int]).sample
    res4: Option[List[Int]] = None
    seems like that should work
    seems to me like posReal and posInt should just be different things
    Aaron S. Hawley
    @ashawley
    Conceptually, yes, it should work. I've also had reservations about the benefits of the generic numeric implementation over a more literal approach. It was part of the deficiency in #439.
    Zak Patterson
    @zakpatterson
    Alright, I understand the tradeoff there, thanks for the explanation.
    Erik Osheim
    @non
    hey i responded on the ticket -- i usually try to stay away from gitter so i'll continue the discussion there
    Zak Patterson
    @zakpatterson
    Sounds good -- I was about to drop the issue but now it sounds there's appetite to fix this behavior, #569 is up @ashawley , let me know if you have thoughts there.
    Dario Abdulrehman
    @dabd
    Hi, yesterday I watched this excellent talk by Erik Osheim and in this slide https://youtu.be/UlZGZh7hfbs?t=3571 he mentions that is important to pay attention to a generator's distribution and range. The library offers Prop.classify and Prop.collect to look at the distribution, but how can we check the range. And what is the exact meaning of range of a generator in Scalacheck?
    Aaron S. Hawley
    @ashawley
    If I had to guess, Erik is using the statistical definition of "range", not to be confused with the one in mathematics.
    https://en.wikipedia.org/wiki/Range_(statistics)
    Dario Abdulrehman
    @dabd
    Sounds like a complex definition. Especially when applied to generators for complex non-numeric data.
    Pierangelo Cecchetto
    @pierangeloc

    Hi, I have a question about how shrinking works (I’m using scalacheck with scalatest)

    class ScalacheckTest extends WordSpec with GeneratorDrivenPropertyChecks {
      "scalacheck" should {
        "shrink" in {
          forAll {
            for {
              n <- Gen.choose(0, 1000)
              m <- Gen.choose(10, 10 + 2 * n)
            } yield (n, m)
          } {
            case (n, m) =>
              fail()
          }
        }
      }
    }

    the test of course fails, but for the minimal input for which it fails I get

    Occurred when passed generated values (
        arg0 = (0,0) // 18 shrinks
      )

    but (0, 0) is clearly not a vaild product of my generators, where the second argument should be always >= 10
    Is there a way to get a minimal sample that respects the generation policy?

    Charles O'Farrell
    @charleso

    @pierangeloc I'm afraid there's an issue in ScalaCheck where forAll picks up the global Shrink, but you're using a local/constrained Gen. If you were just using ScalaCheck directly I would suggest using forAllNoShrink. I believe that function doesn't exist in ScalaTest, so you might have to override the implicit shrink:

    http://blog.knutwalker.de/2014/01/fun-with-scalatests-propertychecks.html

    implicit val noShrink: Shrink[Int] = Shrink.shrinkAny
    Charles O'Farrell
    @charleso
    Unfortunately in ScalaCheck the shrinking is defined separately from the generators. You have to define them in lock-step (ie define your own Arbitrary and Shrink implicits, but that becomes very tedious). There isn't an easy fix, the internals of Gen have to be changed quite significantly. The Hedghehog library (shameless plug, does this in an "integrated" way.
    But as a strong word of warning it's a new library and doesn't have the user base or support that ScalaCheck does. For example there isn't ScalaTest integration. I have just released 0.1.0, but it almost certainly needs more polish to make it more user friendly.
    I hope all of that helps :)
    Pierangelo Cecchetto
    @pierangeloc
    surely it does! Thanks @charleso