@@ -17,104 +17,94 @@ By the end, you will:
1717
1818- Completion of [ Tutorial 5: DeriveGen Signatures] ( t05-derivegen-signatures.md )
1919- Understanding of dependent pairs and indexed types
20+ - Idris2 source file ` ./src/Playground.idr ` with the header:
2021
21- ---
22+ ``` idris
23+ import Deriving.DepTyCheck.Gen
24+ import System.Random.Pure.StdGen
25+ ```
2226
2327## Step 1: Define a SortedList Type
2428
2529First, let's define a list type that is guaranteed to be sorted. The key is that the ` SCons ` constructor requires a proof that adding a new element maintains sortedness.
2630
27- 1 . ** Create a new file** named ` SortedListTutorial.idr ` .
28-
29- 2 . ** Add the following code.**
30-
31- ``` idris
32- module SortedListTutorial
33-
34- import Deriving.DepTyCheck.Gen
35- import Data.Fuel
36-
37- import Test.DepTyCheck.Gen
38- import Test.DepTyCheck.Runner
39- import System.Random.Pure.StdGen
40-
41- data SortedList : Type where
42- SNil : SortedList
43- SCons : (x : Nat) -> (xs : SortedList) -> {auto prf : isSorted (x :: toList xs)} -> SortedList
44-
45- toList : SortedList -> List Nat
46- toList SNil = []
47- toList (SCons x xs prf) = x :: toList xs
48-
49- isSorted : List Nat -> Bool
50- isSorted [] = True
51- isSorted [_] = True
52- isSorted (x :: y :: xs) = x <= y && isSorted (y :: xs)
53- ```
31+ ``` idris
32+ isSorted : List Nat -> Bool
33+ isSorted [] = True
34+ isSorted [_] = True
35+ isSorted (x :: y :: xs) = x <= y && isSorted (y :: xs)
36+
37+ mutual
38+ data SortedList : Type where
39+ SNil : SortedList
40+ SCons : (x : Nat) -> (xs : SortedList) -> {auto prf : So $ isSorted (x :: toList xs)} -> SortedList
41+
42+ toList : SortedList -> List Nat
43+ toList SNil = []
44+ toList (SCons x xs) = x :: toList xs
45+ ```
5446
55- The `SCons` constructor has an **auto-implicit** argument `{auto prf : isSorted (x :: toList xs)}`. This means:
56- - To construct an `SCons`, Idris must find a proof that the list is sorted.
57- - The `auto` keyword tells Idris to search for this proof automatically.
47+ The ` SCons ` constructor has an ** auto-implicit** argument ` {auto prf : isSorted (x :: toList xs)} ` . This means:
48+ - To construct an ` SCons ` , Idris must find a proof that the list is sorted.
49+ - The ` auto ` keyword tells Idris to search for this proof automatically.
5850
5951---
6052
6153## Step 2: Derive the Generator
6254
6355Now comes the magic. We derive a generator for ` SortedList ` with a single line.
6456
65- 1. ** Add the derived generator.**
57+ ### Add the derived generator
6658
67- ```idris
68- genSortedList : Fuel -> Gen MaybeEmpty SortedList
69- genSortedList = deriveGen
70- ```
59+ ``` idris
60+ genSortedList : Fuel -> Gen MaybeEmpty SortedList
61+ genSortedList = deriveGen
62+ ```
7163
72- That's it! No manual logic, no special handling for the proof argument. `deriveGen` will:
73- 1. Generate a candidate `x : Nat`
74- 2. Recursively generate `xs : SortedList`
75- 3. Check if `isSorted (x :: toList xs)` can be proven
76- 4. If yes, construct the `SCons`; if no, try another `x`
64+ That's it! No manual logic, no special handling for the proof argument. ` deriveGen ` will:
65+ 1 . Generate a candidate ` x : Nat `
66+ 2 . Recursively generate ` xs : SortedList `
67+ 3 . Check if ` isSorted (x :: toList xs) ` can be proven
68+ 4 . If yes, construct the ` SCons ` ; if no, try another ` x `
7769
7870---
7971
8072## Step 3: Run and Verify
8173
8274Let's test the generator and verify that all output is sorted.
8375
84- 1. **Add a `main` function.**
85-
86- ```idris
87- main : IO ()
88- main = do
89- putStrLn "--- Generating 5 sorted lists ---"
90- replicate 5 $ do
91- Just sl <- pick1 (genSortedList (limit 20))
92- | Nothing => printLn "Generation failed"
93- let asList = toList sl
94- putStrLn $ "Generated: " ++ show asList
95- putStrLn $ "Is sorted: " ++ show (isSorted asList)
96- ```
76+ ``` idris
77+ testList : HasIO io => io ()
78+ testList = do
79+ putStrLn "--- Generating 5 sorted lists ---"
80+ for_ (the (List Int) [1..5]) $ \_ => do
81+ Just sl <- pick (genSortedList (limit 5))
82+ | Nothing => printLn "Generation failed"
83+ let asList = toList sl
84+ putStrLn $ "Generated: " ++ show asList
85+ putStrLn $ "Is sorted: " ++ show (isSorted asList)
86+ ```
9787
98- 2. ** Compile and run.**
88+ ### Compile and run
9989
10090 ```bash
101- idris2 --build SortedListTutorial.idr
102- ./build/exec/SortedListTutorial
91+ echo -e ':exec testList' | rlwrap pack repl ./src/Playground.idr
10392 ```
10493
105- 3. ** Analyze the output.**
94+ ### Analyze the output
10695
10796 ```
108- --- Generating 5 sorted lists ---
109- Generated: [0, 3, 5, 12, 42]
97+ ...
98+ Main> --- Generating 5 sorted lists ---
99+ Generated: [2, 3, 3, 4, 4]
110100 Is sorted: True
111- Generated: [8, 8, 9, 15, 21, 33 ]
101+ Generated: [2, 2 ]
112102 Is sorted: True
113- Generated: []
103+ Generated: [3, 4 ]
114104 Is sorted: True
115- Generated: [2, 7, 19 ]
105+ Generated: [2]
116106 Is sorted: True
117- Generated: [1, 1, 4, 10, 10, 15 ]
107+ Generated: [4 ]
118108 Is sorted: True
119109 ```
120110
@@ -126,7 +116,7 @@ Let's test the generator and verify that all output is sorted.
126116
127117How does ` deriveGen ` solve the proof constraint? The key is in the ** search order** and ** backtracking** .
128118
129- When `deriveGen` encounters `{auto prf : isSorted (x :: toList xs)}`, it:
119+ When ` deriveGen ` encounters ` {auto prf : So $ isSorted (x :: toList xs)} ` , it:
130120
1311211 . ** Generates candidates** for ` x ` from the default ` Nat ` generator
1321222 . ** Recursively generates** ` xs : SortedList ` (which is already sorted by construction)
@@ -135,30 +125,39 @@ When `deriveGen` encounters `{auto prf : isSorted (x :: toList xs)}`, it:
135125
136126This is why the generator may be slower for complex constraints — it may need multiple attempts to find valid values.
137127
138- ### The Role of Fuel
139-
140- The `Fuel` parameter controls how deep the recursion can go. With more fuel, `deriveGen` can generate longer sorted lists:
128+ The ` Fuel ` parameter controls how deep the recursion can go, so, with more fuel, ` deriveGen ` can generate longer sorted lists:
141129
142130``` idris
143131-- Add to main:
144- putStrLn "\n--- With small fuel (limit 3) ---"
145- Just sl1 <- pick1 (genSortedList (limit 3))
146- | Nothing => printLn "Failed"
147- printLn $ toList sl1
148-
149- putStrLn "\n--- With large fuel (limit 10) ---"
150- Just sl2 <- pick1 (genSortedList (limit 10))
151- | Nothing => printLn "Failed"
152- printLn $ toList sl2
132+ testFuel : IO ()
133+ testFuel = do
134+ putStrLn "\n--- With small fuel (limit 3) ---"
135+ Just sl1 <- pick (genSortedList (limit 3))
136+ | Nothing => printLn "Generation failed"
137+ printLn $ toList sl1
138+
139+ putStrLn "\n--- With large fuel (limit 8) ---"
140+ Just sl2 <- pick (genSortedList (limit 8))
141+ | Nothing => printLn "Generation failed"
142+ printLn $ toList sl2
153143```
154144
155- Output:
156- ```
157- --- With small fuel (limit 3) ---
158- [2, 5, 8]
159-
160- --- With large fuel (limit 10) ---
161- [0, 1, 3, 7, 12, 15, 20, 25, 30, 42]
145+ Run it:
146+
147+ ``` bash
148+ echo -e ' :exec testFuel' | rlwrap pack repl ./src/Playground.idr
149+ ...
150+ Main> --- Generating 5 sorted lists ---
151+ Generated: [2, 3, 3, 4, 4]
152+ Is sorted: True
153+ Generated: [2, 2]
154+ Is sorted: True
155+ Generated: [3, 4]
156+ Is sorted: True
157+ Generated: [2]
158+ Is sorted: True
159+ Generated: [4]
160+ Is sorted: True
162161```
163162
164163---
@@ -168,24 +167,35 @@ Output:
168167Let's see another example of proof-carrying data. Consider a type that represents numbers bounded by a given limit:
169168
170169``` idris
171- -- Add to SortedListTutorial.idr
172-
173- data BoundedNat : (limit : Nat) -> Type where
174- MkBounded : (n : Nat) -> {auto prf : n `LT` limit} -> BoundedNat limit
170+ data BoundedNat : (limit' : Nat) -> Type where
171+ MkBounded : (n : Nat) -> {auto prf : n `LT` limit'} -> BoundedNat limit'
175172
176- genBoundedNat : (limit : Nat) -> Fuel -> Gen MaybeEmpty (BoundedNat limit)
177- genBoundedNat limit = deriveGen
173+ genBoundedNat : Fuel -> (limit' : Nat) -> Gen MaybeEmpty (BoundedNat limit' )
174+ genBoundedNat = deriveGen
178175
179176testBounded : IO ()
180177testBounded = do
181- putStrLn "--- Generating BoundedNat with limit 5 ---"
182- replicate 5 $ do
183- Just bn <- pick1 (genBoundedNat 5 (limit 10))
178+ putStrLn "--- Generating BoundedNat with limit'= 5 ---"
179+ for_ (the (List Int) [1..5]) $ \_ => do
180+ Just bn <- pick (genBoundedNat (limit 10) 5 )
184181 | Nothing => printLn "Failed"
185182 case bn of
186183 MkBounded n => putStrLn $ "Generated: " ++ show n ++ " (< 5)"
187184```
188185
186+ Run it:
187+
188+ ``` bash
189+ echo -e ' :exec testBounded' | rlwrap pack repl ./src/Playground.idr
190+ ...
191+ Main> --- Generating BoundedNat with limit' =5 ---
192+ Generated: 4 (< 5)
193+ Generated: 1 (< 5)
194+ Generated: 1 (< 5)
195+ Generated: 3 (< 5)
196+ Generated: 4 (< 5)
197+ ```
198+
189199The `{auto prf : n `LT` limit}` constraint ensures that only values less than the limit are generated. `deriveGen` will automatically search for valid `n` values.
190200
191201---
@@ -196,3 +206,11 @@ Now that you can generate proof-carrying data, you are ready for more advanced t
196206
197207* **Want to integrate handwritten generators?** Continue to **[Mixing Manual and Automatic Generation](t09-mixing-manual-and-automatic.md)** to see how `deriveGen` automatically discovers and uses your custom generators.
198208* **Want to understand the internals?** Continue to **[Under the Hood: Building a deriveGen-like Macro](t08-under-the-hood-a-derivegen-like-macro.md)** to learn how the derivation engine works.
209+
210+ <!-- idris
211+ main : IO ()
212+ main = do
213+ testList
214+ testFuel
215+ testBounded
216+ -->
0 commit comments