Recursive Generation

In some situations, it is required to generate objects that can nest recursively. For example, JSON is an often used data exchange format that consists of various layers of dictionaries (with string keys) and one dimensional arrays, as well as strings, numbers, booleans an Nothing.

In Supposition.jl, we can generate these kinds of recursively nested objects using the Data.Recursive Possibility. For this, we need a generator of a basecase, as well as a function that wraps one generated example in a new layer by returning a new Possibility.

We can construct the Possibility that generates the basecase like so:

using Supposition

strings = Data.Text(Data.AsciiCharacters())
bools = Data.Booleans()
none = Data.Just(nothing)
numbers = Data.Floats{Float64}()
basecase = strings | numbers | bools | none
Supposition.Data.OneOf:

    Produces an element from one of the following with equal probability:

        ∘ Supposition.Data.Text(Supposition.Data.AsciiCharacters(); min_len=0, max_len=10)Supposition.Data.Floats{Float64}(; nans=true, infs=true)Supposition.Data.Booleans()Supposition.Data.Just(nothing)

which gives us a Data.OneOf, a Possibility that can generate any one of the objects generated by the given Possibility.

For wrapping into new layers, we need a function that wraps our basecase Possibility and gives us a new Possibility generating the wrapped objects. For the JSON example, this means we can wrap an object either in a Vector, or a Dict, where the latter has String keys.

Wrapping order

Recursive expects a function that takes a Possibility for generating the children of the wrapper object, which you should pass into a generator. The generator for the wrapper can be any arbitrary Possibility.

Defining that function like so:

function jsonwrap(child)
    vecs = Data.Vectors(child)
    dicts = Data.Dicts(strings, child)
    vecs | dicts
end
jsonwrap (generic function with 1 method)

allows us to construct the Possibility for generating nested JSON-like objects:

json = Data.Recursive(basecase, jsonwrap; max_layers=3)
example(json)
Dict{String, Union{Nothing, Bool, Float64, Dict{String, Union{Nothing, Bool, Float64, String}}, String, Vector{Union{Nothing, Bool, Float64, String}}}} with 5 entries:
  "!"                 => -1.58772e111
  "\e^Y\x1cq\bEj8"    => -4.31286e-135
  "^"                 => Union{Nothing, Bool, Float64, String}[false]
  "\x0f \t;lgC\e\x15" => nothing
  "Y266uYkn6"         => -5.68895e-145