# Tinycheck: A lightweight enumeration-based property testing library

**URL:** <https://discourse.haskell.org/t/tinycheck-a-lightweight-enumeration-based-property-testing-library/13914>\
**Category:** Announcements\
**Created:** [April 10, 2026, 9:53am UTC](https://discourse.haskell.org/t/tinycheck-a-lightweight-enumeration-based-property-testing-library/13914 "2026-04-10T09:53:22Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![turion](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/turion/32/989_2.png) [@turion](https://discourse.haskell.org/u/turion)\
**Post date:** [April 10, 2026, 9:53am UTC](https://discourse.haskell.org/t/tinycheck-a-lightweight-enumeration-based-property-testing-library/13914/1 "2026-04-10T09:53:22Z")

</div>

I recently [started a discussion](https://discourse.haskell.org/t/why-not-use-smallcheck/13860/22) about the state and future of a library that I like a lot, [`smallcheck`](https://hackage.haskell.org/package/smallcheck). The maintainer argues that `smallcheck` is “largely obsolete” in the presence of libraries like `falsify`. I don’t want to repeat the discussion, if you have an opinion it is warmly welcome at the linked thread. I only mention it because it sparked my interest in the topic.

I toyed around and came up with a very simple way to write an enumeration based property testing library. The core ideas are:

1. Generators are just streams of values, ordered by complexity (some measure of “size”) of the data.
2. Generators are combined fairly by interleaving:

```haskell
interleave :: [a] -> [a] -> [a]
interleave [] as = as
interleave (a : as1) as2 = a : interleave as2 as1

```

Idea 1 is the basic tenet of enumeration based property testing and has been studied in the literature and is applied in many libraries. No need for random number generation and RNG seeding, no need for shrinking. As a bonus, testing is deterministic.

Idea 2 just makes it really simple to write generators for any kind of algebraic datatype. Generic instances for `Arbitrary` are fast to write and useful. All kinds of shapes of your data show up early in the test. In contrast to smallcheck, no concept of depth is needed. Generation is very fast and uses little memory. If your test situation needs very specific test data, just interleave it with the standard generator.

I wrapped all of this up and fleshed it out with a bit of AI help (but I checked everything thoroughly). The result is a small library with very few dependencies. There is `tasty` integration. There are a few examples. So far it seems to work really well. If you are looking for a tiny property testing library, have a look at `tinycheck`, and let me know what you think.

> **[tinycheck](https://hackage.haskell.org/package/tinycheck)**
>
> A lightweight enumeration-based property testing library

> **[GitHub - turion/tinycheck](https://github.com/turion/tinycheck/)**
>
> Contribute to turion/tinycheck development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![Bodigrim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/bodigrim/32/1457_2.png) [@Bodigrim](https://discourse.haskell.org/u/Bodigrim)\
**Post date:** [April 13, 2026, 9:26pm UTC](https://discourse.haskell.org/t/tinycheck-a-lightweight-enumeration-based-property-testing-library/13914/2 "2026-04-13T21:26:42Z")

</div>

> [@turion](#):
>
> Generators are just streams of values, ordered by complexity (some measure of “size”) of the data.

I think this is the right choice, yes. SmallCheck migrated from this to streams parametrized by a monad (and interleaving implemented via `LogicT`) between releases 0.6.2 and 1.0, but I don’t know anyone making use of this flexibility. While in theory one can do some cool stuff, e. g., turning SmallCheck into QuickCheck with `instance MonadRandom m => Stream m Foo`, I’ve never seen it happening in practice, and performance consequences of such generalization are obvious.
