typelevel / general

Repository for general Typelevel information, activity and issues
19 stars 8 forks source link

Submitting sonic for typelevel incubator membership #71

Closed melrief closed 7 years ago

melrief commented 7 years ago

I would like to submit sonic as typelevel project. sonic is an implementation of haskell-hedgehog in Scala using typelevel libraries.

About the criteria:

  1. I'm the only developer.
  2. The typelevel CoC is in the README at the Contributing section.
  3. The project uses Cats everywhere. I've used Sonic to learn more about Cats and I intend to use a pure functional style for this library because it works incredibly well for property-based testing libraries.
  4. There is a README.md with instructions on how to use the library.

There is no artifact yet because, if the library is accepted, I'd like to move it under the typelevel groupId so that people could use "org.typelevel" %% "sonic" % <version> instead of my personal groupId. I had already an experience in moving a project to another groupId and I'd like to avoid that if possible 😄 .

rossabaker commented 7 years ago

Thanks, Mario.

I don't speak Haskell very well, but I see a lot of recent hype about hedgehog. I would find it very helpful to see something about when sonic might be used as opposed to scalacheck.

melrief commented 7 years ago

sonic and ScalaCheck are both property-based testing libraries but differ in the way they implement it.

The most important difference is that the shrinking strategy in sonic is generator-based instead of type-based. You don't have a Shrink[T] and a Gen[T] like in ScalaCheck, instead you have a Gen[T] with a shrinking strategy. If you know property-based testing then you know that shrinking plays an important role in finding the "minimal" input for which the property fails. Integrated shrinking together with a set of functions to create new shrinking strategies gives you greater control over what's going on and allows different generators for the same type T to have different shrinking strategies. To better describe what this means, please have a look at Gen.intNoShrink:

def intNoShrink(range: Range[Int]): Gen[Int] =
  generate {
    (size: ZeroOrPositive) => (seed: Seed) =>
      val (x, y) = range.bounds(size)
      seed.nextIntBetween(x, y)._1
}

and then at Gen.int, which is Gen.intNoShrink with a shrinking strategy:

def int(range: Range[Int]): Gen[Int] =
  intNoShrink(range).shrink(towards(range.origin, _))

this means that the main shrinking strategy for int is to generate values "closer and closer" to range.origin, e.g. 0. Now, you may want to create your own shrinking strategy for your Gen[Int] because the shrinking strategy of Gen.int is not what you are searching for. In sonic, you can do it by just creating a new generator and adding the shrinking strategy that you like:

def int2(range: Range[Int]): Gen[Int] =
  intNoShrink(range).shrink(myShrinkStrategyForInt)

An interesting aspect of this is that shrinking strategies of existing generators can be used by generators that are derived from those. For instance:

def element[A](xs: NonEmptyVector[A]): Gen[A] =
  int(Range.constant(0, xs.length - 1)).map(i => xs.getUnsafe(i))

Here we are not defining any shrink strategy but because int shrinks towards the range.origin and because Range.constant has origin equal to the first parameter, we can deduce that the shrinking strategy for element(xs) is to shrink towards the first element in xs.

Another big difference is Range, which allows to define a Range based on the size of the test for e.g. the length of a list. You have many functions to control how values are generated, given the size of the test. This is also interesting but requires some time to explain because I would first need to explain what a generator is under the hood.

I hope I have given you an idea of what's the difference between the two libraries. sonic is intended to be a more flexible and powerful library for testing.

larsrh commented 7 years ago

Glad to see there's innovation happening in this field. You get a :+1: from me too.

Next steps: You know the drill :smile: (file a PR)

melrief commented 7 years ago

Thanks @larsrh, I've opened #167.