@zio.dev/zio-blocks 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/guides/query-dsl-extending.md +1 -1
- package/guides/query-dsl-fluent-builder.md +1 -1
- package/guides/query-dsl-reified-optics.md +1 -1
- package/guides/query-dsl-sql.md +1 -1
- package/guides/zio-schema-migration.md +1195 -0
- package/index.md +21 -12
- package/package.json +1 -1
- package/reference/allows.md +1377 -0
- package/reference/binding-resolver.md +469 -0
- package/reference/binding.md +1 -1
- package/reference/codec.md +10 -10
- package/reference/docs.md +1 -1
- package/reference/dynamic-optic.md +5 -0
- package/reference/dynamic-schema.md +602 -0
- package/reference/dynamic-value.md +5 -0
- package/reference/json-patch.md +803 -0
- package/reference/json.md +1 -1
- package/reference/media-type.md +2 -2
- package/reference/modifier.md +9 -9
- package/reference/patch.md +4 -0
- package/reference/schema-error.md +569 -0
- package/reference/schema-evolution/as.md +587 -0
- package/reference/schema-evolution/index.md +50 -0
- package/reference/schema-evolution/into.md +1027 -0
- package/reference/schema-expr.md +2 -2
- package/reference/schema.md +29 -0
- package/reference/structural-types.md +369 -0
- package/reference/type-class-derivation.md +329 -324
- package/reference/validation.md +1 -1
- package/reference/xml.md +1304 -0
- package/scope.md +241 -17
- package/sidebars.js +21 -1
- package/reference/schema-evolution.md +0 -540
|
@@ -128,7 +128,7 @@ Given a `Schema[A]`, you can call the `derive` method to get an instance of the
|
|
|
128
128
|
|
|
129
129
|
```scala
|
|
130
130
|
case class Schema[A](reflect: Reflect.Bound[A]) {
|
|
131
|
-
|
|
131
|
+
def derive[D, TC[_]](d: D)(implicit ev: Derivable[D, TC]): TC[A] = ???
|
|
132
132
|
}
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -158,13 +158,7 @@ val result: Either[SchemaError, Person] =
|
|
|
158
158
|
)
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
There is
|
|
162
|
-
|
|
163
|
-
```scala
|
|
164
|
-
case class Schema[A](reflect: Reflect.Bound[A]) {
|
|
165
|
-
def derive[F <: codec.Format](format: F): format.TypeClass[A] = derive(format.deriver)
|
|
166
|
-
}
|
|
167
|
-
```
|
|
161
|
+
There is a `Derivable` type class that enables seamless overloading between `Deriver[TC]` and `Format` arguments, allowing the same `derive` method to work with both:
|
|
168
162
|
|
|
169
163
|
For example, by calling `Person.schema.derive(JsonFormat)`, we can derive a `JsonCodec[Person]` instance:
|
|
170
164
|
|
|
@@ -224,47 +218,39 @@ object DeriveShow extends Deriver[Show] {
|
|
|
224
218
|
modifiers: Seq[Modifier.Reflect],
|
|
225
219
|
defaultValue: Option[A],
|
|
226
220
|
examples: Seq[A]
|
|
227
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] =
|
|
221
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] = {
|
|
222
|
+
// Pre-compute structural setup outside Lazy
|
|
223
|
+
val fieldNames = fields.map(_.name)
|
|
224
|
+
// Cast fields to use Binding as F (we are going to create Reflect.Record with Binding as F)
|
|
225
|
+
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
226
|
+
// Cast to Binding.Record to access constructor/deconstructor
|
|
227
|
+
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
228
|
+
// Build a Reflect.Record to get access to the computed registers for each field
|
|
229
|
+
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
228
230
|
Lazy {
|
|
229
|
-
// Collecting Lazy[Show] instances for each field from the transformed metadata
|
|
230
|
-
val fieldShowInstances: IndexedSeq[(String, Lazy[Show[Any]])] = fields.map { field =>
|
|
231
|
-
val fieldName = field.name
|
|
232
|
-
// Get the Lazy[Show] instance for this field's type, but we won't force it yet
|
|
233
|
-
// We'll force it later when we actually need to show a value of this field
|
|
234
|
-
val fieldShowInstance = D.instance(field.value.metadata).asInstanceOf[Lazy[Show[Any]]]
|
|
235
|
-
(fieldName, fieldShowInstance)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Cast fields to use Binding as F (we are going to create Reflect.Record with Binding as F)
|
|
239
|
-
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
240
|
-
|
|
241
|
-
// Cast to Binding.Record to access constructor/deconstructor
|
|
242
|
-
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
243
|
-
|
|
244
|
-
// Build a Reflect.Record to get access to the computed registers for each field
|
|
245
|
-
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
246
|
-
|
|
247
231
|
new Show[A] {
|
|
232
|
+
// Defer child-instance resolution to first show() call via lazy val.
|
|
233
|
+
// For recursive types (e.g. case class Tree(children: List[Tree])), accessing
|
|
234
|
+
// field.value.metadata during derivation would re-enter a Deferred node that is
|
|
235
|
+
// still initializing, causing an infinite loop. The lazy val here ensures access
|
|
236
|
+
// only happens after the framework has finished deriving all instances.
|
|
237
|
+
private lazy val resolvedShows: IndexedSeq[Show[Any]] =
|
|
238
|
+
fields.map(field => D.instance(field.value.metadata).asInstanceOf[Lazy[Show[Any]]].force)
|
|
248
239
|
def show(value: A): String = {
|
|
249
|
-
|
|
250
240
|
// Create registers with space for all used registers to hold deconstructed field values
|
|
251
241
|
val registers = Registers(recordReflect.usedRegisters)
|
|
252
|
-
|
|
253
242
|
// Deconstruct field values of the record into the registers
|
|
254
243
|
recordBinding.deconstructor.deconstruct(registers, RegisterOffset.Zero, value)
|
|
255
|
-
|
|
256
244
|
// Build string representations for all fields
|
|
257
245
|
val fieldStrings = fields.indices.map { i =>
|
|
258
|
-
val
|
|
259
|
-
|
|
260
|
-
val result = s"$fieldName = ${showInstanceLazy.force.show(fieldValue)}"
|
|
261
|
-
result
|
|
246
|
+
val fieldValue = recordReflect.registers(i).get(registers, RegisterOffset.Zero)
|
|
247
|
+
s"${fieldNames(i)} = ${resolvedShows(i).show(fieldValue)}"
|
|
262
248
|
}
|
|
263
|
-
|
|
264
249
|
s"${typeId.name}(${fieldStrings.mkString(", ")})"
|
|
265
250
|
}
|
|
266
251
|
}
|
|
267
252
|
}
|
|
253
|
+
}
|
|
268
254
|
|
|
269
255
|
override def deriveVariant[F[_, _], A](
|
|
270
256
|
cases: IndexedSeq[Term[F, A, ?]],
|
|
@@ -274,29 +260,28 @@ object DeriveShow extends Deriver[Show] {
|
|
|
274
260
|
modifiers: Seq[Modifier.Reflect],
|
|
275
261
|
defaultValue: Option[A],
|
|
276
262
|
examples: Seq[A]
|
|
277
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] =
|
|
278
|
-
//
|
|
279
|
-
|
|
263
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] = {
|
|
264
|
+
// Collect the Lazy[Show] references for each case outside the Lazy block.
|
|
265
|
+
// Capturing Lazy refs is safe here; we only .force them later inside the lazy val.
|
|
266
|
+
val caseShowLazies: IndexedSeq[Lazy[Show[Any]]] = cases.map { case_ =>
|
|
280
267
|
D.instance(case_.value.metadata).asInstanceOf[Lazy[Show[Any]]]
|
|
281
268
|
}
|
|
282
|
-
|
|
283
269
|
// Cast binding to Binding.Variant to access discriminator and matchers
|
|
284
270
|
val variantBinding = binding.asInstanceOf[Binding.Variant[A]]
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
caseShowInstances(caseIndex).force.show(caseValue)
|
|
271
|
+
Lazy {
|
|
272
|
+
new Show[A] {
|
|
273
|
+
// Force child instances lazily — same recursive-safety rationale as deriveRecord
|
|
274
|
+
private lazy val resolvedShows: IndexedSeq[Show[Any]] = caseShowLazies.map(_.force)
|
|
275
|
+
// Implement show by using discriminator and matchers to find the right case
|
|
276
|
+
// The `value` parameter is of type A (the variant type), e.g. a Shape value
|
|
277
|
+
def show(value: A): String = {
|
|
278
|
+
// Use discriminator to determine which case this value belongs to
|
|
279
|
+
val caseIndex = variantBinding.discriminator.discriminate(value)
|
|
280
|
+
// Use matcher to downcast to the specific case type
|
|
281
|
+
val caseValue = variantBinding.matchers(caseIndex).downcastOrNull(value)
|
|
282
|
+
// Delegate to the case's Show instance — it already knows its own name
|
|
283
|
+
resolvedShows(caseIndex).show(caseValue)
|
|
284
|
+
}
|
|
300
285
|
}
|
|
301
286
|
}
|
|
302
287
|
}
|
|
@@ -309,21 +294,19 @@ object DeriveShow extends Deriver[Show] {
|
|
|
309
294
|
modifiers: Seq[Modifier.Reflect],
|
|
310
295
|
defaultValue: Option[C[A]],
|
|
311
296
|
examples: Seq[C[A]]
|
|
312
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[C[A]]] =
|
|
313
|
-
// Get Show instance for element type LAZILY
|
|
314
|
-
val elementShowLazy: Lazy[Show[A]] = D.instance(element.metadata)
|
|
315
|
-
|
|
297
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[C[A]]] = {
|
|
316
298
|
// Cast binding to Binding.Seq to access the deconstructor
|
|
317
|
-
val
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
299
|
+
val deconstructor = binding.asInstanceOf[Binding.Seq[C, A]].deconstructor
|
|
300
|
+
// Sequences are structurally non-recursive, so we can use monadic .map composition.
|
|
301
|
+
// instance(...).map { elementShow => ... } returns a Lazy that, when forced, builds
|
|
302
|
+
// a Show[C[A]] with elementShow already resolved — no .force needed at show()-time.
|
|
303
|
+
D.instance(element.metadata).map { elementShow =>
|
|
304
|
+
new Show[C[A]] {
|
|
305
|
+
def show(value: C[A]): String = {
|
|
306
|
+
// Use deconstructor to iterate over elements and show each one
|
|
307
|
+
val elements = deconstructor.deconstruct(value).map(elementShow.show)
|
|
308
|
+
s"[${elements.mkString(", ")}]"
|
|
309
|
+
}
|
|
327
310
|
}
|
|
328
311
|
}
|
|
329
312
|
}
|
|
@@ -337,26 +320,22 @@ object DeriveShow extends Deriver[Show] {
|
|
|
337
320
|
modifiers: Seq[Modifier.Reflect],
|
|
338
321
|
defaultValue: Option[M[K, V]],
|
|
339
322
|
examples: Seq[M[K, V]]
|
|
340
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[M[K, V]]] =
|
|
341
|
-
// Get Show instances for key and value types LAZILY
|
|
342
|
-
val keyShowLazy: Lazy[Show[K]] = D.instance(key.metadata)
|
|
343
|
-
val valueShowLazy: Lazy[Show[V]] = D.instance(value.metadata)
|
|
344
|
-
|
|
323
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[M[K, V]]] = {
|
|
345
324
|
// Cast binding to Binding.Map to access the deconstructor
|
|
346
|
-
val
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
325
|
+
val deconstructor = binding.asInstanceOf[Binding.Map[M, K, V]].deconstructor
|
|
326
|
+
// Maps are non-recursive: use .zip to pair the two child Lazy instances, then .map
|
|
327
|
+
// to build the Show[M[K,V]] with both keyShow and valueShow already resolved.
|
|
328
|
+
D.instance(key.metadata).zip(D.instance(value.metadata)).map { case (keyShow, valueShow) =>
|
|
329
|
+
new Show[M[K, V]] {
|
|
330
|
+
def show(m: M[K, V]): String = {
|
|
331
|
+
// Use deconstructor to iterate over key-value pairs
|
|
332
|
+
val entries = deconstructor.deconstruct(m).map { kv =>
|
|
333
|
+
val k = deconstructor.getKey(kv)
|
|
334
|
+
val v = deconstructor.getValue(kv)
|
|
335
|
+
s"${keyShow.show(k)} -> ${valueShow.show(v)}"
|
|
336
|
+
}.mkString(", ")
|
|
337
|
+
s"Map($entries)"
|
|
338
|
+
}
|
|
360
339
|
}
|
|
361
340
|
}
|
|
362
341
|
}
|
|
@@ -406,17 +385,18 @@ object DeriveShow extends Deriver[Show] {
|
|
|
406
385
|
modifiers: Seq[Modifier.Reflect],
|
|
407
386
|
defaultValue: Option[A],
|
|
408
387
|
examples: Seq[A]
|
|
409
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] =
|
|
410
|
-
//
|
|
411
|
-
val wrappedShowLazy: Lazy[Show[B]] = D.instance(wrapped.metadata)
|
|
412
|
-
|
|
413
|
-
// Cast binding to Binding.Wrapper to access unwrap function
|
|
388
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] = {
|
|
389
|
+
// Cast binding to Binding.Wrapper to access the unwrap function
|
|
414
390
|
val wrapperBinding = binding.asInstanceOf[Binding.Wrapper[A, B]]
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
391
|
+
// Wrappers are non-recursive: use .map so wrappedShow is already resolved
|
|
392
|
+
// when show() is called — no .force needed.
|
|
393
|
+
D.instance(wrapped.metadata).map { wrappedShow =>
|
|
394
|
+
new Show[A] {
|
|
395
|
+
def show(value: A): String = {
|
|
396
|
+
// Unwrap the value to access the underlying type B, then delegate to its Show
|
|
397
|
+
val unwrapped = wrapperBinding.unwrap(value)
|
|
398
|
+
s"${typeId.name}(${wrappedShow.show(unwrapped)})"
|
|
399
|
+
}
|
|
420
400
|
}
|
|
421
401
|
}
|
|
422
402
|
}
|
|
@@ -469,57 +449,50 @@ def deriveRecord[F[_, _], A](
|
|
|
469
449
|
modifiers: Seq[Modifier.Reflect],
|
|
470
450
|
defaultValue: Option[A],
|
|
471
451
|
examples: Seq[A]
|
|
472
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] =
|
|
452
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] = {
|
|
453
|
+
// Pre-compute structural setup outside Lazy — none of this touches Deferred nodes
|
|
454
|
+
val fieldNames = fields.map(_.name)
|
|
455
|
+
// Cast fields to use Binding as F (we are going to create Reflect.Record with Binding as F)
|
|
456
|
+
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
457
|
+
// Cast to Binding.Record to access constructor/deconstructor
|
|
458
|
+
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
459
|
+
// Build a Reflect.Record to get access to the computed registers for each field
|
|
460
|
+
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
473
461
|
Lazy {
|
|
474
|
-
// Collecting Lazy[Show] instances for each field from the transformed metadata
|
|
475
|
-
val fieldShowInstances: IndexedSeq[(String, Lazy[Show[Any]])] = fields.map { field =>
|
|
476
|
-
val fieldName = field.name
|
|
477
|
-
// Get the Lazy[Show] instance for this field's type, but we won't force it yet
|
|
478
|
-
// We'll force it later when we actually need to show a value of this field
|
|
479
|
-
val fieldShowInstance = D.instance(field.value.metadata).asInstanceOf[Lazy[Show[Any]]]
|
|
480
|
-
(fieldName, fieldShowInstance)
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Cast fields to use Binding as F (we are going to create Reflect.Record with Binding as F)
|
|
484
|
-
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
485
|
-
|
|
486
|
-
// Cast to Binding.Record to access constructor/deconstructor
|
|
487
|
-
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
488
|
-
|
|
489
|
-
// Build a Reflect.Record to get access to the computed registers for each field
|
|
490
|
-
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
491
|
-
|
|
492
462
|
new Show[A] {
|
|
463
|
+
// Defer child-instance resolution to first show() call via lazy val.
|
|
464
|
+
// For recursive types (e.g. case class Tree(children: List[Tree])), accessing
|
|
465
|
+
// field.value.metadata during derivation would re-enter a Deferred node that is
|
|
466
|
+
// still initialising, causing an infinite loop. The lazy val here ensures access
|
|
467
|
+
// only happens after the framework has finished deriving all instances.
|
|
468
|
+
private lazy val resolvedShows: IndexedSeq[Show[Any]] =
|
|
469
|
+
fields.map(field => D.instance(field.value.metadata).asInstanceOf[Lazy[Show[Any]]].force)
|
|
493
470
|
def show(value: A): String = {
|
|
494
|
-
|
|
495
471
|
// Create registers with space for all used registers to hold deconstructed field values
|
|
496
472
|
val registers = Registers(recordReflect.usedRegisters)
|
|
497
|
-
|
|
498
473
|
// Deconstruct field values of the record into the registers
|
|
499
474
|
recordBinding.deconstructor.deconstruct(registers, RegisterOffset.Zero, value)
|
|
500
|
-
|
|
501
475
|
// Build string representations for all fields
|
|
502
476
|
val fieldStrings = fields.indices.map { i =>
|
|
503
|
-
val
|
|
504
|
-
|
|
505
|
-
val result = s"$fieldName = ${showInstanceLazy.force.show(fieldValue)}"
|
|
506
|
-
result
|
|
477
|
+
val fieldValue = recordReflect.registers(i).get(registers, RegisterOffset.Zero)
|
|
478
|
+
s"${fieldNames(i)} = ${resolvedShows(i).show(fieldValue)}"
|
|
507
479
|
}
|
|
508
|
-
|
|
509
480
|
s"${typeId.name}(${fieldStrings.mkString(", ")})"
|
|
510
481
|
}
|
|
511
482
|
}
|
|
512
483
|
}
|
|
484
|
+
}
|
|
513
485
|
```
|
|
514
486
|
|
|
515
487
|
The `deriveRecord` method demonstrates derivation mechanics for record types such as case classes and tuples. To derive the type class for a record type, we follow these steps:
|
|
516
|
-
1. First, we
|
|
517
|
-
2. Second, we
|
|
518
|
-
3. Third,
|
|
488
|
+
1. First, we pre-compute structural setup (field names, `Reflect.Record`, binding casts) outside the `Lazy` block — none of this accesses `Deferred` nodes.
|
|
489
|
+
2. Second, inside `Lazy`, we build a `new Show[A]` with a `private lazy val resolvedShows` that resolves child instances on demand.
|
|
490
|
+
3. Third, when `show()` is first called, `resolvedShows` forces the child `Lazy[Show]` instances and then deconstructs the record value into its individual fields.
|
|
491
|
+
4. Finally, we assemble the string representation by combining field names and their representations.
|
|
519
492
|
|
|
520
|
-
During the first step, the
|
|
493
|
+
During the first step, the structural setup is computed outside the `Lazy` block so the `Lazy` thunk itself is lightweight. This separation is important because structural types like `Reflect.Record` don't access `Deferred` schema nodes that might not yet be initialized.
|
|
521
494
|
|
|
522
|
-
|
|
495
|
+
The key insight is the `private lazy val resolvedShows` inside the `new Show[A]` class. It gathers `Lazy[Show]` instances for each field by calling `D.instance(field.value.metadata)`, then immediately forces them. These calls happen lazily — only on the first invocation of `show()` — which is safe because by that time the framework has completed derivation and all `Lazy` values are fully memoized. Without this deferral, recursive types like `case class Tree(value: Int, children: List[Tree])` would cause an infinite loop: deriving `Show[Tree]` requires `Show[List[Tree]]`, which requires `Show[Tree]` again.
|
|
523
496
|
|
|
524
497
|
Our goal is to build a `String` representation of the record in the format `TypeName(field1 = value1, field2 = value2, ...)`. To achieve this, we need to access the individual field values of the record at runtime. To do this, we have to deconstruct the record value, which is given to the `show(value: A)` method, into its individual fields.
|
|
525
498
|
|
|
@@ -527,9 +500,9 @@ To deconstruct the record, we use the `Binding.Record[A]` that was provided as a
|
|
|
527
500
|
|
|
528
501
|
Now we are ready to deconstruct the `A` value, using the `Binding.Record#deconstructor.deconstruct(registers, RegisterOffset.Zero, value)` call, which extracts the field values of the record into this register buffer in a single pass. Now the field values are stored in `registers`.
|
|
529
502
|
|
|
530
|
-
The next question is how we can access the field values from the registers? The `Reflect.Record` we built earlier also computes the register layout for each field, which allows us to retrieve each field value from the appropriate register slot using `recordReflect.registers(i).get(registers, RegisterOffset.Zero)`. This call accesses the `i`-th field's value from the registers based on the register layout computed by `Reflect.Record`.
|
|
503
|
+
The next question is how we can access the field values from the registers? The `Reflect.Record` we built earlier also computes the register layout for each field, which allows us to retrieve each field value from the appropriate register slot using `recordReflect.registers(i).get(registers, RegisterOffset.Zero)`. This call accesses the `i`-th field's value from the registers based on the register layout computed by `Reflect.Record`.
|
|
531
504
|
|
|
532
|
-
Finally, we
|
|
505
|
+
Finally, we iterate through each field, retrieve its value from the registers, look up the already-resolved `Show` instance for that field's type from `resolvedShows`, and format the result as `fieldName = fieldValue`. The output assembles into the familiar `TypeName(field1 = value1, field2 = value2)` representation.
|
|
533
506
|
|
|
534
507
|
### Variant Derivation
|
|
535
508
|
|
|
@@ -544,33 +517,36 @@ def deriveVariant[F[_, _], A](
|
|
|
544
517
|
modifiers: Seq[Modifier.Reflect],
|
|
545
518
|
defaultValue: Option[A],
|
|
546
519
|
examples: Seq[A]
|
|
547
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] =
|
|
548
|
-
//
|
|
549
|
-
|
|
520
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] = {
|
|
521
|
+
// Collect the Lazy[Show] references for each case outside the Lazy block.
|
|
522
|
+
// Capturing Lazy refs is safe here; we only .force them later inside the lazy val.
|
|
523
|
+
val caseShowLazies: IndexedSeq[Lazy[Show[Any]]] = cases.map { case_ =>
|
|
550
524
|
D.instance(case_.value.metadata).asInstanceOf[Lazy[Show[Any]]]
|
|
551
525
|
}
|
|
552
526
|
// Cast binding to Binding.Variant to access discriminator and matchers
|
|
553
527
|
val variantBinding = binding.asInstanceOf[Binding.Variant[A]]
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
528
|
+
Lazy {
|
|
529
|
+
new Show[A] {
|
|
530
|
+
// Force child instances lazily — same recursive-safety rationale as deriveRecord
|
|
531
|
+
private lazy val resolvedShows: IndexedSeq[Show[Any]] = caseShowLazies.map(_.force)
|
|
532
|
+
// Implement show by using discriminator and matchers to find the right case
|
|
533
|
+
// The `value` parameter is of type A (the variant type), e.g. a Shape value
|
|
534
|
+
def show(value: A): String = {
|
|
535
|
+
// Use discriminator to determine which case this value belongs to
|
|
536
|
+
val caseIndex = variantBinding.discriminator.discriminate(value)
|
|
537
|
+
// Use matcher to downcast to the specific case type
|
|
538
|
+
val caseValue = variantBinding.matchers(caseIndex).downcastOrNull(value)
|
|
539
|
+
// Delegate to the case's Show instance — it already knows its own name
|
|
540
|
+
resolvedShows(caseIndex).show(caseValue)
|
|
541
|
+
}
|
|
566
542
|
}
|
|
567
543
|
}
|
|
568
544
|
}
|
|
569
545
|
```
|
|
570
546
|
|
|
571
|
-
The derivation process for variants is similar to records, but instead of fields, we have cases.
|
|
547
|
+
The derivation process for variants is similar to records, but instead of fields, we have cases. The `Lazy[Show]` references for all cases are collected outside the `Lazy` block — this is safe because it only captures references without forcing them. Inside `Lazy`, a `private lazy val resolvedShows` forces all the child instances on the first `show()` call, applying the same recursive-safety guarantee as in `deriveRecord`.
|
|
572
548
|
|
|
573
|
-
|
|
549
|
+
At runtime, we use the discriminator to determine which case the value belongs to, and then the matcher to downcast the value to the specific case type. Finally, we look up the already-resolved `Show` instance for that case from `resolvedShows` and delegate to it. Each case's `Show` instance already knows how to format itself (including its own type name), so no additional formatting is needed at the variant level.
|
|
574
550
|
|
|
575
551
|
### Sequence Derivation
|
|
576
552
|
|
|
@@ -585,25 +561,25 @@ def deriveSequence[F[_, _], C[_], A](
|
|
|
585
561
|
modifiers: Seq[Modifier.Reflect],
|
|
586
562
|
defaultValue: Option[C[A]],
|
|
587
563
|
examples: Seq[C[A]]
|
|
588
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[C[A]]] =
|
|
589
|
-
// Get Show instance for element type (lazily)
|
|
590
|
-
val elementShowLazy: Lazy[Show[A]] = D.instance(element.metadata)
|
|
564
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[C[A]]] = {
|
|
591
565
|
// Cast binding to Binding.Seq to access the deconstructor
|
|
592
|
-
val
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
566
|
+
val deconstructor = binding.asInstanceOf[Binding.Seq[C, A]].deconstructor
|
|
567
|
+
// Sequences are structurally non-recursive, so we can use monadic .map composition.
|
|
568
|
+
// instance(...).map { elementShow => ... } returns a Lazy that, when forced, builds
|
|
569
|
+
// a Show[C[A]] with elementShow already resolved — no .force needed at show()-time.
|
|
570
|
+
D.instance(element.metadata).map { elementShow =>
|
|
571
|
+
new Show[C[A]] {
|
|
572
|
+
def show(value: C[A]): String = {
|
|
573
|
+
// Use deconstructor to iterate over elements and show each one
|
|
574
|
+
val elements = deconstructor.deconstruct(value).map(elementShow.show)
|
|
575
|
+
s"[${elements.mkString(", ")}]"
|
|
576
|
+
}
|
|
601
577
|
}
|
|
602
578
|
}
|
|
603
579
|
}
|
|
604
580
|
```
|
|
605
581
|
|
|
606
|
-
The derivation process for sequences is straightforward.
|
|
582
|
+
The derivation process for sequences is straightforward. Because sequences are structurally non-recursive, we can use monadic `Lazy` composition via `.map` instead of the `private lazy val` pattern needed for records and variants. `D.instance(element.metadata).map { elementShow => ... }` returns a `Lazy[Show[C[A]]]` that, when forced, produces a `Show[C[A]]` with `elementShow` already resolved — no explicit `.force` is needed inside `show()`. At runtime, we use the deconstructor to iterate over the elements of the sequence and call `elementShow.show` on each one. Finally, we combine all element representations into a bracketed string.
|
|
607
583
|
|
|
608
584
|
### Map Derivation
|
|
609
585
|
|
|
@@ -619,32 +595,28 @@ def deriveMap[F[_, _], M[_, _], K, V](
|
|
|
619
595
|
modifiers: Seq[Modifier.Reflect],
|
|
620
596
|
defaultValue: Option[M[K, V]],
|
|
621
597
|
examples: Seq[M[K, V]]
|
|
622
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[M[K, V]]] =
|
|
623
|
-
// Get Show instances for key and value types LAZILY
|
|
624
|
-
val keyShowLazy: Lazy[Show[K]] = D.instance(key.metadata)
|
|
625
|
-
val valueShowLazy: Lazy[Show[V]] = D.instance(value.metadata)
|
|
626
|
-
|
|
598
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[M[K, V]]] = {
|
|
627
599
|
// Cast binding to Binding.Map to access the deconstructor
|
|
628
|
-
val
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
600
|
+
val deconstructor = binding.asInstanceOf[Binding.Map[M, K, V]].deconstructor
|
|
601
|
+
// Maps are non-recursive: use .zip to pair the two child Lazy instances, then .map
|
|
602
|
+
// to build the Show[M[K,V]] with both keyShow and valueShow already resolved.
|
|
603
|
+
D.instance(key.metadata).zip(D.instance(value.metadata)).map { case (keyShow, valueShow) =>
|
|
604
|
+
new Show[M[K, V]] {
|
|
605
|
+
def show(m: M[K, V]): String = {
|
|
606
|
+
// Use deconstructor to iterate over key-value pairs
|
|
607
|
+
val entries = deconstructor.deconstruct(m).map { kv =>
|
|
608
|
+
val k = deconstructor.getKey(kv)
|
|
609
|
+
val v = deconstructor.getValue(kv)
|
|
610
|
+
s"${keyShow.show(k)} -> ${valueShow.show(v)}"
|
|
611
|
+
}.mkString(", ")
|
|
612
|
+
s"Map($entries)"
|
|
613
|
+
}
|
|
642
614
|
}
|
|
643
615
|
}
|
|
644
616
|
}
|
|
645
617
|
```
|
|
646
618
|
|
|
647
|
-
The derivation process for maps is similar to sequences, but we have
|
|
619
|
+
The derivation process for maps is similar to sequences, but we have two child instances to compose. Maps are non-recursive, so we use `.zip` to pair the two child `Lazy` instances into a single `Lazy[(Show[K], Show[V])]`, then `.map` to build the `Show[M[K,V]]` with both `keyShow` and `valueShow` already resolved. At runtime, we use the deconstructor to iterate over the key-value pairs of the map, calling `keyShow.show` and `valueShow.show` on each pair. No explicit `.force` is needed since `.map` already handles forcing. Finally, we combine all entries into a `Map(...)` string.
|
|
648
620
|
|
|
649
621
|
### Dynamic Derivation
|
|
650
622
|
|
|
@@ -700,23 +672,24 @@ def deriveWrapper[F[_, _], A, B](
|
|
|
700
672
|
modifiers: Seq[Modifier.Reflect],
|
|
701
673
|
defaultValue: Option[A],
|
|
702
674
|
examples: Seq[A]
|
|
703
|
-
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] =
|
|
704
|
-
//
|
|
705
|
-
val wrappedShowLazy: Lazy[Show[B]] = D.instance(wrapped.metadata)
|
|
706
|
-
|
|
707
|
-
// Cast binding to Binding.Wrapper to access unwrap function
|
|
675
|
+
)(implicit F: HasBinding[F], D: DeriveShow.HasInstance[F]): Lazy[Show[A]] = {
|
|
676
|
+
// Cast binding to Binding.Wrapper to access the unwrap function
|
|
708
677
|
val wrapperBinding = binding.asInstanceOf[Binding.Wrapper[A, B]]
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
678
|
+
// Wrappers are non-recursive: use .map so wrappedShow is already resolved
|
|
679
|
+
// when show() is called — no .force needed.
|
|
680
|
+
D.instance(wrapped.metadata).map { wrappedShow =>
|
|
681
|
+
new Show[A] {
|
|
682
|
+
def show(value: A): String = {
|
|
683
|
+
// Unwrap the value to access the underlying type B, then delegate to its Show
|
|
684
|
+
val unwrapped = wrapperBinding.unwrap(value)
|
|
685
|
+
s"${typeId.name}(${wrappedShow.show(unwrapped)})"
|
|
686
|
+
}
|
|
714
687
|
}
|
|
715
688
|
}
|
|
716
689
|
}
|
|
717
690
|
```
|
|
718
691
|
|
|
719
|
-
The derivation process for wrapper types involves unwrapping the value to access the underlying type.
|
|
692
|
+
The derivation process for wrapper types involves unwrapping the value to access the underlying type. Wrappers are non-recursive, so we use `.map` composition: `D.instance(wrapped.metadata).map { wrappedShow => ... }` returns a `Lazy[Show[A]]` that, when forced, produces a `Show[A]` with `wrappedShow` already resolved. At runtime, we use the `unwrap` function from the binding to retrieve the underlying `B` value and delegate to `wrappedShow.show` — no explicit `.force` is needed.
|
|
720
693
|
|
|
721
694
|
### Example Usages
|
|
722
695
|
|
|
@@ -945,34 +918,35 @@ object DeriveGen extends Deriver[Gen] {
|
|
|
945
918
|
modifiers: Seq[Modifier.Reflect],
|
|
946
919
|
defaultValue: Option[A],
|
|
947
920
|
examples: Seq[A]
|
|
948
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] =
|
|
921
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] = {
|
|
922
|
+
// Pre-compute structural setup outside Lazy — none of this touches Deferred nodes
|
|
923
|
+
// Build Reflect.Record to access registers and constructor
|
|
924
|
+
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
925
|
+
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
926
|
+
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
949
927
|
Lazy {
|
|
950
|
-
// Get Gen instances for each field
|
|
951
|
-
val fieldGens: IndexedSeq[Lazy[Gen[Any]]] = fields.map { field =>
|
|
952
|
-
D.instance(field.value.metadata).asInstanceOf[Lazy[Gen[Any]]]
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
// Build Reflect.Record to access registers and constructor
|
|
956
|
-
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
957
|
-
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
958
|
-
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
959
|
-
|
|
960
928
|
new Gen[A] {
|
|
929
|
+
// Defer child-instance resolution to first generate() call via lazy val.
|
|
930
|
+
// For recursive types (e.g. case class Tree(children: List[Tree])), accessing
|
|
931
|
+
// field.value.metadata during derivation would re-enter a Deferred node that is
|
|
932
|
+
// still initialising, causing an infinite loop. The lazy val here ensures access
|
|
933
|
+
// only happens after the framework has finished deriving all instances.
|
|
934
|
+
private lazy val resolvedGens: IndexedSeq[Gen[Any]] =
|
|
935
|
+
fields.map(field => D.instance(field.value.metadata).asInstanceOf[Lazy[Gen[Any]]].force)
|
|
961
936
|
def generate(random: Random): A = {
|
|
962
937
|
// Create registers to hold field values
|
|
963
938
|
val registers = Registers(recordReflect.usedRegisters)
|
|
964
|
-
|
|
965
939
|
// Generate each field and store in registers
|
|
966
940
|
fields.indices.foreach { i =>
|
|
967
|
-
val value =
|
|
941
|
+
val value = resolvedGens(i).generate(random)
|
|
968
942
|
recordReflect.registers(i).set(registers, RegisterOffset.Zero, value)
|
|
969
943
|
}
|
|
970
|
-
|
|
971
944
|
// Construct the record from registers
|
|
972
945
|
recordBinding.constructor.construct(registers, RegisterOffset.Zero)
|
|
973
946
|
}
|
|
974
947
|
}
|
|
975
948
|
}
|
|
949
|
+
}
|
|
976
950
|
|
|
977
951
|
/**
|
|
978
952
|
* Strategy:
|
|
@@ -988,17 +962,21 @@ object DeriveGen extends Deriver[Gen] {
|
|
|
988
962
|
modifiers: Seq[Modifier.Reflect],
|
|
989
963
|
defaultValue: Option[A],
|
|
990
964
|
examples: Seq[A]
|
|
991
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] =
|
|
992
|
-
//
|
|
993
|
-
|
|
965
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] = {
|
|
966
|
+
// Collect the Lazy[Gen] references for each case outside the Lazy block.
|
|
967
|
+
// Capturing Lazy refs is safe here; we only .force them later inside the lazy val.
|
|
968
|
+
val caseGenLazies: IndexedSeq[Lazy[Gen[A]]] = cases.map { c =>
|
|
994
969
|
D.instance(c.value.metadata).asInstanceOf[Lazy[Gen[A]]]
|
|
995
970
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
971
|
+
Lazy {
|
|
972
|
+
new Gen[A] {
|
|
973
|
+
// Force child instances lazily — same recursive-safety rationale as deriveRecord
|
|
974
|
+
private lazy val resolvedGens: IndexedSeq[Gen[A]] = caseGenLazies.map(_.force)
|
|
975
|
+
def generate(random: Random): A = {
|
|
976
|
+
// Pick a random case and generate its value
|
|
977
|
+
val caseIndex = random.nextInt(cases.length)
|
|
978
|
+
resolvedGens(caseIndex).generate(random)
|
|
979
|
+
}
|
|
1002
980
|
}
|
|
1003
981
|
}
|
|
1004
982
|
}
|
|
@@ -1017,24 +995,29 @@ object DeriveGen extends Deriver[Gen] {
|
|
|
1017
995
|
modifiers: Seq[Modifier.Reflect],
|
|
1018
996
|
defaultValue: Option[C[A]],
|
|
1019
997
|
examples: Seq[C[A]]
|
|
1020
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[C[A]]] =
|
|
1021
|
-
|
|
1022
|
-
val
|
|
1023
|
-
val
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
998
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[C[A]]] = {
|
|
999
|
+
// Cast binding to Binding.Seq to access the constructor
|
|
1000
|
+
val constructor = binding.asInstanceOf[Binding.Seq[C, A]].constructor
|
|
1001
|
+
val elemClassTag = element.typeId.classTag.asInstanceOf[scala.reflect.ClassTag[A]]
|
|
1002
|
+
// Sequences are structurally non-recursive, so we can use monadic .map composition.
|
|
1003
|
+
// instance(...).map { elementGen => ... } returns a Lazy that, when forced, builds
|
|
1004
|
+
// a Gen[C[A]] with elementGen already resolved — no .force needed at generate()-time.
|
|
1005
|
+
D.instance(element.metadata).map { elementGen =>
|
|
1006
|
+
new Gen[C[A]] {
|
|
1007
|
+
def generate(random: Random): C[A] = {
|
|
1008
|
+
val length = random.nextInt(6) // 0 to 5 elements
|
|
1009
|
+
implicit val ct: scala.reflect.ClassTag[A] = elemClassTag
|
|
1010
|
+
|
|
1011
|
+
if (length == 0) {
|
|
1012
|
+
constructor.empty[A]
|
|
1013
|
+
} else {
|
|
1014
|
+
// Build the collection by generating each element and adding it to the builder
|
|
1015
|
+
val builder = constructor.newBuilder[A](length)
|
|
1016
|
+
(0 until length).foreach { _ =>
|
|
1017
|
+
constructor.add(builder, elementGen.generate(random))
|
|
1018
|
+
}
|
|
1019
|
+
constructor.result(builder)
|
|
1036
1020
|
}
|
|
1037
|
-
constructor.result(builder)
|
|
1038
1021
|
}
|
|
1039
1022
|
}
|
|
1040
1023
|
}
|
|
@@ -1055,24 +1038,26 @@ object DeriveGen extends Deriver[Gen] {
|
|
|
1055
1038
|
modifiers: Seq[Modifier.Reflect],
|
|
1056
1039
|
defaultValue: Option[M[K, V]],
|
|
1057
1040
|
examples: Seq[M[K, V]]
|
|
1058
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[M[K, V]]] =
|
|
1059
|
-
|
|
1060
|
-
val
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1041
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[M[K, V]]] = {
|
|
1042
|
+
// Cast binding to Binding.Map to access the constructor
|
|
1043
|
+
val constructor = binding.asInstanceOf[Binding.Map[M, K, V]].constructor
|
|
1044
|
+
// Maps are non-recursive: use .zip to pair the two child Lazy instances, then .map
|
|
1045
|
+
// to build the Gen[M[K,V]] with both keyGen and valueGen already resolved.
|
|
1046
|
+
D.instance(key.metadata).zip(D.instance(value.metadata)).map { case (keyGen, valueGen) =>
|
|
1047
|
+
new Gen[M[K, V]] {
|
|
1048
|
+
def generate(random: Random): M[K, V] = {
|
|
1049
|
+
val size = random.nextInt(6) // 0 to 5 entries
|
|
1050
|
+
|
|
1051
|
+
if (size == 0) {
|
|
1052
|
+
constructor.emptyObject[K, V]
|
|
1053
|
+
} else {
|
|
1054
|
+
// Build the map by generating each key-value pair and adding it to the builder
|
|
1055
|
+
val builder = constructor.newObjectBuilder[K, V](size)
|
|
1056
|
+
(0 until size).foreach { _ =>
|
|
1057
|
+
constructor.addObject(builder, keyGen.generate(random), valueGen.generate(random))
|
|
1058
|
+
}
|
|
1059
|
+
constructor.resultObject(builder)
|
|
1074
1060
|
}
|
|
1075
|
-
constructor.resultObject(builder)
|
|
1076
1061
|
}
|
|
1077
1062
|
}
|
|
1078
1063
|
}
|
|
@@ -1144,13 +1129,17 @@ object DeriveGen extends Deriver[Gen] {
|
|
|
1144
1129
|
modifiers: Seq[Modifier.Reflect],
|
|
1145
1130
|
defaultValue: Option[A],
|
|
1146
1131
|
examples: Seq[A]
|
|
1147
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] =
|
|
1148
|
-
|
|
1132
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] = {
|
|
1133
|
+
// Cast binding to Binding.Wrapper to access the wrap function
|
|
1149
1134
|
val wrapperBinding = binding.asInstanceOf[Binding.Wrapper[A, B]]
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1135
|
+
// Wrappers are non-recursive: use .map so wrappedGen is already resolved
|
|
1136
|
+
// when generate() is called — no .force needed.
|
|
1137
|
+
D.instance(wrapped.metadata).map { wrappedGen =>
|
|
1138
|
+
new Gen[A] {
|
|
1139
|
+
def generate(random: Random): A =
|
|
1140
|
+
// Generate a value of the underlying type B and wrap it into A
|
|
1141
|
+
wrapperBinding.wrap(wrappedGen.generate(random))
|
|
1142
|
+
}
|
|
1154
1143
|
}
|
|
1155
1144
|
}
|
|
1156
1145
|
}
|
|
@@ -1203,37 +1192,38 @@ def deriveRecord[F[_, _], A](
|
|
|
1203
1192
|
modifiers: Seq[Modifier.Reflect],
|
|
1204
1193
|
defaultValue: Option[A],
|
|
1205
1194
|
examples: Seq[A]
|
|
1206
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] =
|
|
1195
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] = {
|
|
1196
|
+
// Pre-compute structural setup outside Lazy — none of this touches Deferred nodes
|
|
1197
|
+
// Build Reflect.Record to access registers and constructor
|
|
1198
|
+
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
1199
|
+
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
1200
|
+
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
1207
1201
|
Lazy {
|
|
1208
|
-
// Get Gen instances for each field
|
|
1209
|
-
val fieldGens: IndexedSeq[Lazy[Gen[Any]]] = fields.map { field =>
|
|
1210
|
-
D.instance(field.value.metadata).asInstanceOf[Lazy[Gen[Any]]]
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
// Build Reflect.Record to access registers and constructor
|
|
1214
|
-
val recordFields = fields.asInstanceOf[IndexedSeq[Term[Binding, A, ?]]]
|
|
1215
|
-
val recordBinding = binding.asInstanceOf[Binding.Record[A]]
|
|
1216
|
-
val recordReflect = new Reflect.Record[Binding, A](recordFields, typeId, recordBinding, doc, modifiers)
|
|
1217
|
-
|
|
1218
1202
|
new Gen[A] {
|
|
1203
|
+
// Defer child-instance resolution to first generate() call via lazy val.
|
|
1204
|
+
// For recursive types (e.g. case class Tree(children: List[Tree])), accessing
|
|
1205
|
+
// field.value.metadata during derivation would re-enter a Deferred node that is
|
|
1206
|
+
// still initialising, causing an infinite loop. The lazy val here ensures access
|
|
1207
|
+
// only happens after the framework has finished deriving all instances.
|
|
1208
|
+
private lazy val resolvedGens: IndexedSeq[Gen[Any]] =
|
|
1209
|
+
fields.map(field => D.instance(field.value.metadata).asInstanceOf[Lazy[Gen[Any]]].force)
|
|
1219
1210
|
def generate(random: Random): A = {
|
|
1220
1211
|
// Create registers to hold field values
|
|
1221
1212
|
val registers = Registers(recordReflect.usedRegisters)
|
|
1222
|
-
|
|
1223
1213
|
// Generate each field and store in registers
|
|
1224
1214
|
fields.indices.foreach { i =>
|
|
1225
|
-
val value =
|
|
1215
|
+
val value = resolvedGens(i).generate(random)
|
|
1226
1216
|
recordReflect.registers(i).set(registers, RegisterOffset.Zero, value)
|
|
1227
1217
|
}
|
|
1228
|
-
|
|
1229
1218
|
// Construct the record from registers
|
|
1230
1219
|
recordBinding.constructor.construct(registers, RegisterOffset.Zero)
|
|
1231
1220
|
}
|
|
1232
1221
|
}
|
|
1233
1222
|
}
|
|
1223
|
+
}
|
|
1234
1224
|
```
|
|
1235
1225
|
|
|
1236
|
-
As shown above, the implementation of the `deriveRecord` method for `Gen` is
|
|
1226
|
+
As shown above, the implementation of the `deriveRecord` method for `Gen` follows the same structure as its `Show` counterpart. The structural setup (field casts, `Reflect.Record`) is done outside `Lazy` to keep the thunk lightweight, and `resolvedGens` is a `private lazy val` that defers child-instance resolution until the first `generate()` call for recursive-type safety. The primary difference from `Show` is the data flow: instead of deconstructing an existing record to read its fields, we generate random values for each field via `resolvedGens(i).generate(random)`, store them in registers using `Register#set`, and then invoke the `constructor` from the `Binding` to create an instance of type `A`.
|
|
1237
1227
|
|
|
1238
1228
|
### Variant Derivation
|
|
1239
1229
|
|
|
@@ -1248,23 +1238,27 @@ def deriveVariant[F[_, _], A](
|
|
|
1248
1238
|
modifiers: Seq[Modifier.Reflect],
|
|
1249
1239
|
defaultValue: Option[A],
|
|
1250
1240
|
examples: Seq[A]
|
|
1251
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] =
|
|
1252
|
-
//
|
|
1253
|
-
|
|
1241
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] = {
|
|
1242
|
+
// Collect the Lazy[Gen] references for each case outside the Lazy block.
|
|
1243
|
+
// Capturing Lazy refs is safe here; we only .force them later inside the lazy val.
|
|
1244
|
+
val caseGenLazies: IndexedSeq[Lazy[Gen[A]]] = cases.map { c =>
|
|
1254
1245
|
D.instance(c.value.metadata).asInstanceOf[Lazy[Gen[A]]]
|
|
1255
1246
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1247
|
+
Lazy {
|
|
1248
|
+
new Gen[A] {
|
|
1249
|
+
// Force child instances lazily — same recursive-safety rationale as deriveRecord
|
|
1250
|
+
private lazy val resolvedGens: IndexedSeq[Gen[A]] = caseGenLazies.map(_.force)
|
|
1251
|
+
def generate(random: Random): A = {
|
|
1252
|
+
// Pick a random case and generate its value
|
|
1253
|
+
val caseIndex = random.nextInt(cases.length)
|
|
1254
|
+
resolvedGens(caseIndex).generate(random)
|
|
1255
|
+
}
|
|
1262
1256
|
}
|
|
1263
1257
|
}
|
|
1264
1258
|
}
|
|
1265
1259
|
```
|
|
1266
1260
|
|
|
1267
|
-
The derivation process for `Gen` variants
|
|
1261
|
+
The derivation process for `Gen` variants applies the same deferral pattern as `deriveRecord`. The `caseGenLazies` are collected outside the `Lazy` block — capturing `Lazy` references is safe — and `resolvedGens` forces them lazily on the first `generate()` call. At runtime, we randomly select one of the resolved `Gen` instances and call `generate` on it. This is simpler than the record case because there are no registers or constructors involved — each case's `Gen` already knows how to produce a complete value of its type.
|
|
1268
1262
|
|
|
1269
1263
|
### Sequence Derivation
|
|
1270
1264
|
|
|
@@ -1279,31 +1273,36 @@ def deriveSequence[F[_, _], C[_], A](
|
|
|
1279
1273
|
modifiers: Seq[Modifier.Reflect],
|
|
1280
1274
|
defaultValue: Option[C[A]],
|
|
1281
1275
|
examples: Seq[C[A]]
|
|
1282
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[C[A]]] =
|
|
1283
|
-
|
|
1284
|
-
val
|
|
1285
|
-
val
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1276
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[C[A]]] = {
|
|
1277
|
+
// Cast binding to Binding.Seq to access the constructor
|
|
1278
|
+
val constructor = binding.asInstanceOf[Binding.Seq[C, A]].constructor
|
|
1279
|
+
val elemClassTag = element.typeId.classTag.asInstanceOf[scala.reflect.ClassTag[A]]
|
|
1280
|
+
// Sequences are structurally non-recursive, so we can use monadic .map composition.
|
|
1281
|
+
// instance(...).map { elementGen => ... } returns a Lazy that, when forced, builds
|
|
1282
|
+
// a Gen[C[A]] with elementGen already resolved — no .force needed at generate()-time.
|
|
1283
|
+
D.instance(element.metadata).map { elementGen =>
|
|
1284
|
+
new Gen[C[A]] {
|
|
1285
|
+
def generate(random: Random): C[A] = {
|
|
1286
|
+
val length = random.nextInt(6) // 0 to 5 elements
|
|
1287
|
+
implicit val ct: scala.reflect.ClassTag[A] = elemClassTag
|
|
1288
|
+
|
|
1289
|
+
if (length == 0) {
|
|
1290
|
+
constructor.empty[A]
|
|
1291
|
+
} else {
|
|
1292
|
+
// Build the collection by generating each element and adding it to the builder
|
|
1293
|
+
val builder = constructor.newBuilder[A](length)
|
|
1294
|
+
(0 until length).foreach { _ =>
|
|
1295
|
+
constructor.add(builder, elementGen.generate(random))
|
|
1296
|
+
}
|
|
1297
|
+
constructor.result(builder)
|
|
1298
1298
|
}
|
|
1299
|
-
constructor.result(builder)
|
|
1300
1299
|
}
|
|
1301
1300
|
}
|
|
1302
1301
|
}
|
|
1303
1302
|
}
|
|
1304
1303
|
```
|
|
1305
1304
|
|
|
1306
|
-
A sequence is an object that contains multiple elements of the same type.
|
|
1305
|
+
A sequence is an object that contains multiple elements of the same type. Because sequences are non-recursive, we use `.map` composition: `D.instance(element.metadata).map { elementGen => ... }` returns a `Lazy[Gen[C[A]]]` that, when forced, builds a `Gen[C[A]]` with `elementGen` already resolved — no explicit `.force` is needed inside `generate()`. At runtime, we generate a random length (0–5). For an empty length, we return `constructor.empty`. Otherwise, we create a new builder using `constructor.newBuilder`, generate random element values with `elementGen.generate(random)`, add them with `constructor.add`, and finalise with `constructor.result`.
|
|
1307
1306
|
|
|
1308
1307
|
### Map Derivation
|
|
1309
1308
|
|
|
@@ -1319,31 +1318,33 @@ def deriveMap[F[_, _], M[_, _], K, V](
|
|
|
1319
1318
|
modifiers: Seq[Modifier.Reflect],
|
|
1320
1319
|
defaultValue: Option[M[K, V]],
|
|
1321
1320
|
examples: Seq[M[K, V]]
|
|
1322
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[M[K, V]]] =
|
|
1323
|
-
|
|
1324
|
-
val
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1321
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[M[K, V]]] = {
|
|
1322
|
+
// Cast binding to Binding.Map to access the constructor
|
|
1323
|
+
val constructor = binding.asInstanceOf[Binding.Map[M, K, V]].constructor
|
|
1324
|
+
// Maps are non-recursive: use .zip to pair the two child Lazy instances, then .map
|
|
1325
|
+
// to build the Gen[M[K,V]] with both keyGen and valueGen already resolved.
|
|
1326
|
+
D.instance(key.metadata).zip(D.instance(value.metadata)).map { case (keyGen, valueGen) =>
|
|
1327
|
+
new Gen[M[K, V]] {
|
|
1328
|
+
def generate(random: Random): M[K, V] = {
|
|
1329
|
+
val size = random.nextInt(6) // 0 to 5 entries
|
|
1330
|
+
|
|
1331
|
+
if (size == 0) {
|
|
1332
|
+
constructor.emptyObject[K, V]
|
|
1333
|
+
} else {
|
|
1334
|
+
// Build the map by generating each key-value pair and adding it to the builder
|
|
1335
|
+
val builder = constructor.newObjectBuilder[K, V](size)
|
|
1336
|
+
(0 until size).foreach { _ =>
|
|
1337
|
+
constructor.addObject(builder, keyGen.generate(random), valueGen.generate(random))
|
|
1338
|
+
}
|
|
1339
|
+
constructor.resultObject(builder)
|
|
1338
1340
|
}
|
|
1339
|
-
constructor.resultObject(builder)
|
|
1340
1341
|
}
|
|
1341
1342
|
}
|
|
1342
1343
|
}
|
|
1343
1344
|
}
|
|
1344
1345
|
```
|
|
1345
1346
|
|
|
1346
|
-
The derivation process for maps is similar to sequences
|
|
1347
|
+
The derivation process for maps is similar to sequences but requires composing two child instances. We use `.zip` to combine the `Lazy[Gen[K]]` and `Lazy[Gen[V]]` into a single `Lazy`, then `.map` to build the `Gen[M[K,V]]` with both `keyGen` and `valueGen` already resolved. At runtime, a random size (0–5) determines whether we return an empty map or build one entry by entry using `constructor.addObject(builder, keyGen.generate(random), valueGen.generate(random))`.
|
|
1347
1348
|
|
|
1348
1349
|
### Dynamic Derivation
|
|
1349
1350
|
|
|
@@ -1419,18 +1420,22 @@ def deriveWrapper[F[_, _], A, B](
|
|
|
1419
1420
|
modifiers: Seq[Modifier.Reflect],
|
|
1420
1421
|
defaultValue: Option[A],
|
|
1421
1422
|
examples: Seq[A]
|
|
1422
|
-
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] =
|
|
1423
|
-
|
|
1423
|
+
)(implicit F: HasBinding[F], D: DeriveGen.HasInstance[F]): Lazy[Gen[A]] = {
|
|
1424
|
+
// Cast binding to Binding.Wrapper to access the wrap function
|
|
1424
1425
|
val wrapperBinding = binding.asInstanceOf[Binding.Wrapper[A, B]]
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1426
|
+
// Wrappers are non-recursive: use .map so wrappedGen is already resolved
|
|
1427
|
+
// when generate() is called — no .force needed.
|
|
1428
|
+
D.instance(wrapped.metadata).map { wrappedGen =>
|
|
1429
|
+
new Gen[A] {
|
|
1430
|
+
def generate(random: Random): A =
|
|
1431
|
+
// Generate a value of the underlying type B and wrap it into A
|
|
1432
|
+
wrapperBinding.wrap(wrappedGen.generate(random))
|
|
1433
|
+
}
|
|
1429
1434
|
}
|
|
1430
1435
|
}
|
|
1431
1436
|
```
|
|
1432
1437
|
|
|
1433
|
-
|
|
1438
|
+
Wrappers are non-recursive, so we use `.map` composition: `D.instance(wrapped.metadata).map { wrappedGen => ... }` returns a `Lazy[Gen[A]]` that, when forced, builds a `Gen[A]` with `wrappedGen` already resolved. Within `generate()`, we generate a random value of the underlying type `B` and wrap it into `A` using the `wrap` function from the binding — no explicit `.force` is needed.
|
|
1434
1439
|
|
|
1435
1440
|
### Example Usages
|
|
1436
1441
|
|
|
@@ -1451,7 +1456,7 @@ Now we can use the derived `Gen[Person]` instance to generate random `Person` va
|
|
|
1451
1456
|
|
|
1452
1457
|
```scala
|
|
1453
1458
|
val random = new Random(42) // Seeded for reproducible output
|
|
1454
|
-
// random: Random = scala.util.Random@
|
|
1459
|
+
// random: Random = scala.util.Random@5cfafbe0
|
|
1455
1460
|
|
|
1456
1461
|
Person.gen.generate(random)
|
|
1457
1462
|
// res14: Person = Person(name = "p", age = -1360544799)
|