elm-explorations / elm-explorations/test

Idea: Use `Range` to create (some) Fuzzers

Open
#89 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Elm
Stars
244
Forks
40
Avg merge
1d 23h
Merged PRs (30d)
2

Description

I'm a fan of [`hedgehog`](https://hackage.haskell.org/package/hedgehog) [1] and I think it gets a lot of things right.

One of those is having a `Range` parameter for all fuzzers (_i.e._ `Gen`s) that generate values with some notion of a range: integers, floats, strings, lists and other collection types.

Thus, there is no `int : Fuzzer Int`.
Only `int : Range Int -> Fuzzer Int`.

In `hedgehog`, internally a `Range` is defined as (translated to Elm):
```elm
type Size
= Size Int

type Range a
= Range a (Size -> ( a, a ))
```
Here `Size` is _essentially_ the number of runs.
In `Range a boundsFunction`:
1. `a` is the "origin" value
2. `boundsFunction` shrinks towards this "origin" `a` as `Size` goes down (example defaults are `0` for `Int`s, empty list for lists)

See [2] for a comment about how I understand `Size` being used in `boundsFunction`.

To construct a `Range Int`, you would use:
```elm
linear : Int -> Int -> Range Int
linear lo hi =
linearFrom lo lo hi

linearFrom : Int -> Int -> Int -> Range Int
linearFrom origin lo hi =
Range origin (intBoundsFunction origin lo hi)

intBoundsFunction : Int -> Int -> Int -> (Size -> (Int, Int))
-- Not elaborated on purpose, you can see hedgehog source for how it's done in Haskell
```

IMO this has several advantages:
1. You get shrinking "for free" as part of the `boundsFunction`, it's useful to look at the `hedgehog` source [3] to see how they do this using `linear`, `linearFrom`, `exponential` and `exponentialFrom` constructor functions.
1. You end up with a more flexible and powerful API.
1. It forces users to think about the range of values they want to generate, so that it matches the problem domain instead of assuming defaults. Both in terms of total range, but also the distribution of values.

I understand that a more flexible and powerful API also results in a more "complicated" API, and you might be trying to go with something simple and uncomplicated.

Hence posting this issue to gauge appetite.

* [1] They also have a more general website with links to packages in other languages: https://hedgehog.qa/
* [2] From a quick scan of the code, it appears if `runs >= 99` the full range is generated, otherwise it is scaled down. So basically the range saturates at `Size 99`.
* [3] https://hackage.haskell.org/package/hedgehog-0.6.1/docs/src/Hedgehog.Internal.Range.html#Range

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.