|
| 1 | +# 5. Mixing Manual and Automatic Generation |
| 2 | + |
| 3 | +In the [previous tutorial](04-automatic-generator-derivation.md), we saw how `deriveGen` can automatically create generators for complex types. We also saw how to give it "hints" for primitive types like `String` using the `=>` syntax. |
| 4 | + |
| 5 | +But what if you have a custom data type that needs a special, handwritten generator? `DepTyCheck` provides a powerful, "magic" feature: if you define a generator for a type, `deriveGen` will automatically find it and use it. |
| 6 | + |
| 7 | +### Our Goal |
| 8 | + |
| 9 | +We will define a `SpecialString` newtype and write a custom generator for it. We will then create a larger `User` type that contains a `SpecialString` and see how `deriveGen` automatically discovers and integrates our manual generator without any special syntax. |
| 10 | + |
| 11 | +### Prerequisites |
| 12 | + |
| 13 | +This tutorial assumes you have completed [Tutorial 4: Automatic Generator Derivation](04-automatic-generator-derivation.md). |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Step 1: A Type That Needs a Custom Generator |
| 18 | + |
| 19 | +Imagine we have a `SpecialString` type that should only ever contain specific, predefined values (e.g., usernames with special privileges). A fully random `String` generator is not appropriate here. |
| 20 | + |
| 21 | +1. **Create a new file** named `Mixed.idr`. |
| 22 | + |
| 23 | +2. **Define the types and a handwritten generator.** |
| 24 | + |
| 25 | + ```idris |
| 26 | + module Mixed |
| 27 | +
|
| 28 | + import Test.DepTyCheck.Gen |
| 29 | + import Test.DepTyCheck.Runner |
| 30 | + import Data.Fuel |
| 31 | +
|
| 32 | + -- A type that needs special generation |
| 33 | + data SpecialString = MkSpecialString String |
| 34 | +
|
| 35 | + -- Standard domain types |
| 36 | + data User = MkUser SpecialString Nat |
| 37 | +
|
| 38 | + -- A handwritten generator for SpecialString |
| 39 | + genSpecialString : Fuel -> Gen MaybeEmpty SpecialString |
| 40 | + genSpecialString _ = map MkSpecialString (elements ["admin", "root", "system"]) |
| 41 | + ``` |
| 42 | + We have defined `genSpecialString` to only produce three possible values. Notice it has the standard `Fuel -> Gen MaybeEmpty ...` signature. |
| 43 | +
|
| 44 | +## Step 2: Automatic Derivation Finds the Manual Generator |
| 45 | +
|
| 46 | +Now, let's define a generator for `User` using `deriveGen`. A `User` contains a `SpecialString` and a `Nat`. `deriveGen` knows how to generate a `Nat` by default. What will it do for `SpecialString`? |
| 47 | +
|
| 48 | +1. **Add the derived generator** to `Mixed.idr`. |
| 49 | +
|
| 50 | + ```idris |
| 51 | + %language ElabReflection |
| 52 | +
|
| 53 | + -- Add deriveGen for the User |
| 54 | + genUser : Fuel -> Gen MaybeEmpty User |
| 55 | + genUser = deriveGen |
| 56 | + ``` |
| 57 | +
|
| 58 | +2. **Test it in the REPL.** Load your `Mixed.idr` file. |
| 59 | +
|
| 60 | + ```idris |
| 61 | + :exec pick1 (genUser (limit 10)) |
| 62 | + -- MkUser (MkSpecialString "root") 5 : User |
| 63 | +
|
| 64 | + :exec pick1 (genUser (limit 10)) |
| 65 | + -- MkUser (MkSpecialString "admin") 12 : User |
| 66 | + ``` |
| 67 | +
|
| 68 | +It works! `deriveGen` automatically found and used our `genSpecialString` function when it needed to produce a `SpecialString`. It did this simply by looking for a function in the current scope with the required type signature (`... -> Gen ... SpecialString`). |
| 69 | +
|
| 70 | +## When to Use Implicit vs. Explicit (`=>`) Generation |
| 71 | +
|
| 72 | +`DepTyCheck` now offers two ways to combine generators: |
| 73 | +
|
| 74 | +1. **Implicit (this tutorial):** `deriveGen` automatically finds a generator in scope for a specific custom type (`SpecialString`). |
| 75 | + * **When to use:** This is the best method when you have a custom data type and you *always* want to use one specific generator for it. |
| 76 | +
|
| 77 | +2. **Explicit (`=>` syntax):** As seen in Tutorial 4, you can add a `(Fuel -> Gen MaybeEmpty String) =>` constraint to the signature. |
| 78 | + * **When to use:** This is best when you want to provide a generator for a *primitive* or *built-in* type (like `String`, `Nat`, `List`), or when you want the caller to be able to supply *different* generators in different contexts. |
| 79 | +
|
| 80 | +## Congratulations! |
| 81 | +
|
| 82 | +You've learned how to seamlessly mix automatic and manual generation, giving you the perfect balance of convenience and control. |
| 83 | +
|
| 84 | +### Next Steps |
| 85 | +
|
| 86 | +* **Continue to the next tutorial:** [Generating GADTs and Proof-Carrying Data](./06_generating_gadts.md) to see how these techniques apply to even more advanced types. |
0 commit comments