Skip to content

What's the point of applicative-style generator creation? #13

@00-kat

Description

@00-kat

The README has this example (albeit slightly modified since int_uniform_inclusive doesn't exist anymore):

fn box_generator() {
  qcheck.return({
    use x <- qcheck.parameter
    use y <- qcheck.parameter
    use w <- qcheck.parameter
    use h <- qcheck.parameter
    Box(x:, y:, w:, h:)
  })
  |> qcheck.apply(qcheck.bounded_int(-100, 100))
  |> qcheck.apply(qcheck.bounded_int(-100, 100))
  |> qcheck.apply(qcheck.bounded_int(1, 100))
  |> qcheck.apply(qcheck.bounded_int(1, 100))
}

This can just be written using map+bind (i.e. in a monadic fashion), and it is cleaner and considerably less error-prone:

fn box_generator() {
  use x <- qcheck.bind(qcheck.bounded_int(-100, 100))
  use y <- qcheck.bind(qcheck.bounded_int(-100, 100))
  use w <- qcheck.bind(qcheck.bounded_int(1, 100))
  use h <- qcheck.map(qcheck.bounded_int(1, 100))
  Box(x:, y:, w:, h:)
}

What benefits does the applicative API have? I don't see how composed generators being independent of each other is useful for Generators; is it something to do with shrinking?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions