@zio.dev/zio-blocks 0.0.1
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/index.md +426 -0
- package/package.json +6 -0
- package/path-interpolator.md +645 -0
- package/reference/binding.md +364 -0
- package/reference/chunk.md +576 -0
- package/reference/context.md +157 -0
- package/reference/docs.md +524 -0
- package/reference/dynamic-value.md +823 -0
- package/reference/formats.md +640 -0
- package/reference/json-schema.md +626 -0
- package/reference/json.md +979 -0
- package/reference/modifier.md +276 -0
- package/reference/optics.md +1613 -0
- package/reference/patch.md +631 -0
- package/reference/reflect-transform.md +387 -0
- package/reference/reflect.md +521 -0
- package/reference/registers.md +282 -0
- package/reference/schema-evolution.md +540 -0
- package/reference/schema.md +619 -0
- package/reference/syntax.md +409 -0
- package/reference/type-class-derivation-internals.md +632 -0
- package/reference/typeid.md +900 -0
- package/reference/validation.md +458 -0
- package/scope.md +627 -0
- package/sidebars.js +30 -0
|
@@ -0,0 +1,900 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: typeid
|
|
3
|
+
title: "TypeId"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeId
|
|
7
|
+
|
|
8
|
+
`TypeId[A]` represents the identity of a type or type constructor at runtime. It provides rich type identity information including the type's name, owner (package/class/object), type parameters, classification (nominal, alias, or opaque), parent types, and annotations.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
TypeId is fundamental to ZIO Blocks' schema system, enabling:
|
|
13
|
+
|
|
14
|
+
- **Type identification** - Uniquely identify types across serialization boundaries
|
|
15
|
+
- **Subtype checking** - Determine inheritance relationships at runtime
|
|
16
|
+
- **Type normalization** - Resolve type aliases to their underlying types
|
|
17
|
+
- **Schema derivation** - Automatically derive schemas for user-defined types
|
|
18
|
+
|
|
19
|
+
```scala
|
|
20
|
+
import zio.blocks.typeid._
|
|
21
|
+
|
|
22
|
+
// Derive TypeId for your types
|
|
23
|
+
case class Person(name: String, age: Int)
|
|
24
|
+
val personId: TypeId[Person] = TypeId.of[Person]
|
|
25
|
+
|
|
26
|
+
// Access type information
|
|
27
|
+
personId.name // "Person"
|
|
28
|
+
personId.fullName // "com.example.Person"
|
|
29
|
+
personId.isCaseClass // true
|
|
30
|
+
|
|
31
|
+
// Use predefined TypeIds
|
|
32
|
+
TypeId.int.fullName // "scala.Int"
|
|
33
|
+
TypeId.string.fullName // "java.lang.String"
|
|
34
|
+
TypeId.list.arity // 1 (type constructor)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
TypeId is included in the `zio-blocks-typeid` module. Add it to your build:
|
|
40
|
+
|
|
41
|
+
```scala
|
|
42
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-typeid" % "<version>"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Cross-platform support: TypeId works on JVM and Scala.js.
|
|
46
|
+
|
|
47
|
+
## Creating TypeIds
|
|
48
|
+
|
|
49
|
+
### Automatic Derivation
|
|
50
|
+
|
|
51
|
+
The simplest way to get a TypeId is via macro derivation:
|
|
52
|
+
|
|
53
|
+
```scala
|
|
54
|
+
import zio.blocks.typeid._
|
|
55
|
+
|
|
56
|
+
case class User(id: Long, email: String)
|
|
57
|
+
|
|
58
|
+
// Scala 3
|
|
59
|
+
val userId: TypeId[User] = TypeId.of[User]
|
|
60
|
+
|
|
61
|
+
// Scala 2
|
|
62
|
+
val userId: TypeId[User] = TypeId.of[User]
|
|
63
|
+
|
|
64
|
+
// Or use implicit derivation
|
|
65
|
+
val userId: TypeId[User] = implicitly[TypeId[User]]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The macro extracts complete type information including:
|
|
69
|
+
- Type name and owner
|
|
70
|
+
- Type parameters and variance
|
|
71
|
+
- Parent types (for sealed traits and enums)
|
|
72
|
+
- Whether it's a case class, sealed trait, enum, etc.
|
|
73
|
+
|
|
74
|
+
### Manual Construction
|
|
75
|
+
|
|
76
|
+
For manual type registration or testing, use smart constructors:
|
|
77
|
+
|
|
78
|
+
```scala
|
|
79
|
+
// Nominal types (classes, traits, objects)
|
|
80
|
+
val myTypeId = TypeId.nominal[MyType](
|
|
81
|
+
name = "MyType",
|
|
82
|
+
owner = Owner.fromPackagePath("com.example"),
|
|
83
|
+
defKind = TypeDefKind.Class(isCase = true)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Type aliases
|
|
87
|
+
val aliasId = TypeId.alias[Age](
|
|
88
|
+
name = "Age",
|
|
89
|
+
owner = Owner.fromPackagePath("com.example"),
|
|
90
|
+
aliased = TypeRepr.Ref(TypeId.int)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
// Opaque types (Scala 3)
|
|
94
|
+
val emailId = TypeId.opaque[Email](
|
|
95
|
+
name = "Email",
|
|
96
|
+
owner = Owner.fromPackagePath("com.example"),
|
|
97
|
+
representation = TypeRepr.Ref(TypeId.string)
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Applied Types
|
|
102
|
+
|
|
103
|
+
Create applied types (type constructors with arguments):
|
|
104
|
+
|
|
105
|
+
```scala
|
|
106
|
+
// List[Int]
|
|
107
|
+
val listIntId = TypeId.applied[List[Int]](
|
|
108
|
+
TypeId.list,
|
|
109
|
+
TypeRepr.Ref(TypeId.int)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
// Map[String, Int]
|
|
113
|
+
val mapId = TypeId.applied[Map[String, Int]](
|
|
114
|
+
TypeId.map,
|
|
115
|
+
TypeRepr.Ref(TypeId.string),
|
|
116
|
+
TypeRepr.Ref(TypeId.int)
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## TypeId Properties
|
|
121
|
+
|
|
122
|
+
### Basic Properties
|
|
123
|
+
|
|
124
|
+
```scala
|
|
125
|
+
val id = TypeId.of[Person]
|
|
126
|
+
|
|
127
|
+
id.name // "Person" - simple name
|
|
128
|
+
id.fullName // "com.example.Person" - fully qualified
|
|
129
|
+
id.owner // Owner representing the package/enclosing type
|
|
130
|
+
id.arity // 0 for proper types, n for type constructors
|
|
131
|
+
id.typeParams // List of TypeParam for type constructors
|
|
132
|
+
id.typeArgs // List of TypeRepr for applied types
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Type Classification
|
|
136
|
+
|
|
137
|
+
```scala
|
|
138
|
+
id.isClass // true for classes
|
|
139
|
+
id.isTrait // true for traits
|
|
140
|
+
id.isObject // true for singleton objects
|
|
141
|
+
id.isEnum // true for Scala 3 enums
|
|
142
|
+
id.isCaseClass // true for case classes
|
|
143
|
+
id.isValueClass // true for value classes (extends AnyVal)
|
|
144
|
+
id.isSealed // true for sealed traits
|
|
145
|
+
id.isAlias // true for type aliases
|
|
146
|
+
id.isOpaque // true for opaque types
|
|
147
|
+
id.isAbstract // true for abstract type members
|
|
148
|
+
|
|
149
|
+
id.isProperType // arity == 0
|
|
150
|
+
id.isTypeConstructor // arity > 0
|
|
151
|
+
id.isApplied // has type arguments
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Common Type Checks
|
|
155
|
+
|
|
156
|
+
```scala
|
|
157
|
+
id.isTuple // scala.TupleN
|
|
158
|
+
id.isProduct // scala.ProductN
|
|
159
|
+
id.isSum // Either or Option
|
|
160
|
+
id.isEither // scala.util.Either
|
|
161
|
+
id.isOption // scala.Option
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Subtype Relationships
|
|
165
|
+
|
|
166
|
+
```scala
|
|
167
|
+
sealed trait Animal
|
|
168
|
+
case class Dog(name: String) extends Animal
|
|
169
|
+
|
|
170
|
+
val dogId = TypeId.of[Dog]
|
|
171
|
+
val animalId = TypeId.of[Animal]
|
|
172
|
+
|
|
173
|
+
dogId.isSubtypeOf(animalId) // true
|
|
174
|
+
animalId.isSupertypeOf(dogId) // true
|
|
175
|
+
dogId.isEquivalentTo(dogId) // true
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Subtype checking handles:
|
|
179
|
+
- Direct inheritance
|
|
180
|
+
- Enum cases and their parent enums
|
|
181
|
+
- Sealed trait subtypes
|
|
182
|
+
- Transitive inheritance
|
|
183
|
+
- Variance-aware subtyping for applied types
|
|
184
|
+
|
|
185
|
+
### Pattern Matching
|
|
186
|
+
|
|
187
|
+
TypeId provides extractors for pattern matching:
|
|
188
|
+
|
|
189
|
+
```scala
|
|
190
|
+
typeId match {
|
|
191
|
+
case TypeId.Nominal(name, owner, params, defKind, parents) =>
|
|
192
|
+
// Regular types
|
|
193
|
+
|
|
194
|
+
case TypeId.Alias(name, owner, params, aliased) =>
|
|
195
|
+
// Type aliases - aliased is the underlying TypeRepr
|
|
196
|
+
|
|
197
|
+
case TypeId.Opaque(name, owner, params, repr, bounds) =>
|
|
198
|
+
// Opaque types - repr is the representation type
|
|
199
|
+
|
|
200
|
+
case TypeId.Sealed(name) =>
|
|
201
|
+
// Sealed traits
|
|
202
|
+
|
|
203
|
+
case TypeId.Enum(name, owner) =>
|
|
204
|
+
// Scala 3 enums
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## TypeRepr
|
|
209
|
+
|
|
210
|
+
`TypeRepr` represents type expressions in the Scala type system. While `TypeId` identifies a type definition, `TypeRepr` represents how types are used in expressions.
|
|
211
|
+
|
|
212
|
+
### Basic Type References
|
|
213
|
+
|
|
214
|
+
```scala
|
|
215
|
+
// Reference to a named type
|
|
216
|
+
TypeRepr.Ref(TypeId.int) // Int
|
|
217
|
+
TypeRepr.Ref(TypeId.string) // String
|
|
218
|
+
|
|
219
|
+
// Reference to a type parameter
|
|
220
|
+
TypeRepr.ParamRef(TypeParam.A) // A
|
|
221
|
+
TypeRepr.ParamRef(param, depth = 1) // nested binder reference
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Applied Types
|
|
225
|
+
|
|
226
|
+
```scala
|
|
227
|
+
// List[Int]
|
|
228
|
+
TypeRepr.Applied(
|
|
229
|
+
TypeRepr.Ref(TypeId.list),
|
|
230
|
+
List(TypeRepr.Ref(TypeId.int))
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
// Map[String, Int]
|
|
234
|
+
TypeRepr.Applied(
|
|
235
|
+
TypeRepr.Ref(TypeId.map),
|
|
236
|
+
List(TypeRepr.Ref(TypeId.string), TypeRepr.Ref(TypeId.int))
|
|
237
|
+
)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Compound Types
|
|
241
|
+
|
|
242
|
+
```scala
|
|
243
|
+
// Intersection: A & B (Scala 3) or A with B (Scala 2)
|
|
244
|
+
TypeRepr.Intersection(List(typeA, typeB))
|
|
245
|
+
|
|
246
|
+
// Union: A | B (Scala 3 only)
|
|
247
|
+
TypeRepr.Union(List(typeA, typeB))
|
|
248
|
+
|
|
249
|
+
// Convenience constructors handle edge cases
|
|
250
|
+
TypeRepr.intersection(List(typeA)) // returns typeA (not Intersection)
|
|
251
|
+
TypeRepr.intersection(Nil) // returns AnyType
|
|
252
|
+
TypeRepr.union(List(typeA)) // returns typeA
|
|
253
|
+
TypeRepr.union(Nil) // returns NothingType
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Function Types
|
|
257
|
+
|
|
258
|
+
```scala
|
|
259
|
+
// A => B
|
|
260
|
+
TypeRepr.Function(List(typeA), typeB)
|
|
261
|
+
|
|
262
|
+
// (A, B) => C
|
|
263
|
+
TypeRepr.Function(List(typeA, typeB), typeC)
|
|
264
|
+
|
|
265
|
+
// (A, B) ?=> C (context function, Scala 3)
|
|
266
|
+
TypeRepr.ContextFunction(List(typeA, typeB), typeC)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Tuple Types
|
|
270
|
+
|
|
271
|
+
```scala
|
|
272
|
+
// (A, B, C)
|
|
273
|
+
TypeRepr.Tuple(List(
|
|
274
|
+
TupleElement(None, typeA),
|
|
275
|
+
TupleElement(None, typeB),
|
|
276
|
+
TupleElement(None, typeC)
|
|
277
|
+
))
|
|
278
|
+
|
|
279
|
+
// Named tuples (Scala 3.5+): (name: String, age: Int)
|
|
280
|
+
TypeRepr.Tuple(List(
|
|
281
|
+
TupleElement(Some("name"), TypeRepr.Ref(TypeId.string)),
|
|
282
|
+
TupleElement(Some("age"), TypeRepr.Ref(TypeId.int))
|
|
283
|
+
))
|
|
284
|
+
|
|
285
|
+
// Convenience for unnamed tuples
|
|
286
|
+
TypeRepr.tuple(List(typeA, typeB, typeC))
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Structural Types
|
|
290
|
+
|
|
291
|
+
```scala
|
|
292
|
+
// { def foo: Int }
|
|
293
|
+
TypeRepr.Structural(
|
|
294
|
+
parents = Nil,
|
|
295
|
+
members = List(
|
|
296
|
+
Member.Def("foo", Nil, Nil, TypeRepr.Ref(TypeId.int))
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
// AnyRef { type T; val x: T }
|
|
301
|
+
TypeRepr.Structural(
|
|
302
|
+
parents = List(TypeRepr.Ref(anyRefId)),
|
|
303
|
+
members = List(
|
|
304
|
+
Member.TypeMember("T"),
|
|
305
|
+
Member.Val("x", TypeRepr.ParamRef(paramT))
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Path-Dependent and Singleton Types
|
|
311
|
+
|
|
312
|
+
```scala
|
|
313
|
+
// x.type (singleton type)
|
|
314
|
+
TypeRepr.Singleton(TermPath.fromOwner(owner, "x"))
|
|
315
|
+
|
|
316
|
+
// this.type
|
|
317
|
+
TypeRepr.ThisType(owner)
|
|
318
|
+
|
|
319
|
+
// Outer#Inner (type projection)
|
|
320
|
+
TypeRepr.TypeProjection(outerType, "Inner")
|
|
321
|
+
|
|
322
|
+
// qualifier.Member (type selection)
|
|
323
|
+
TypeRepr.TypeSelect(qualifierType, "Member")
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Special Types
|
|
327
|
+
|
|
328
|
+
```scala
|
|
329
|
+
TypeRepr.AnyType // Any
|
|
330
|
+
TypeRepr.NothingType // Nothing
|
|
331
|
+
TypeRepr.NullType // Null
|
|
332
|
+
TypeRepr.UnitType // Unit
|
|
333
|
+
TypeRepr.AnyKindType // AnyKind (for kind-polymorphic contexts)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Constant/Literal Types
|
|
337
|
+
|
|
338
|
+
```scala
|
|
339
|
+
TypeRepr.Constant.IntConst(42) // 42 (literal type)
|
|
340
|
+
TypeRepr.Constant.StringConst("foo") // "foo"
|
|
341
|
+
TypeRepr.Constant.BooleanConst(true) // true
|
|
342
|
+
TypeRepr.Constant.ClassOfConst(tpe) // classOf[T]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Type Lambdas (Scala 3)
|
|
346
|
+
|
|
347
|
+
```scala
|
|
348
|
+
// [X] =>> F[X]
|
|
349
|
+
TypeRepr.TypeLambda(
|
|
350
|
+
params = List(TypeParam("X", 0)),
|
|
351
|
+
body = TypeRepr.Applied(
|
|
352
|
+
TypeRepr.ParamRef(paramF),
|
|
353
|
+
List(TypeRepr.ParamRef(paramX))
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Wildcards and Bounds
|
|
359
|
+
|
|
360
|
+
```scala
|
|
361
|
+
// ?
|
|
362
|
+
TypeRepr.Wildcard()
|
|
363
|
+
|
|
364
|
+
// ? <: Upper
|
|
365
|
+
TypeRepr.Wildcard(TypeBounds.upper(upperType))
|
|
366
|
+
|
|
367
|
+
// ? >: Lower
|
|
368
|
+
TypeRepr.Wildcard(TypeBounds.lower(lowerType))
|
|
369
|
+
|
|
370
|
+
// ? >: Lower <: Upper
|
|
371
|
+
TypeRepr.Wildcard(TypeBounds(lowerType, upperType))
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Parameter Modifiers
|
|
375
|
+
|
|
376
|
+
```scala
|
|
377
|
+
// => A (by-name)
|
|
378
|
+
TypeRepr.ByName(typeA)
|
|
379
|
+
|
|
380
|
+
// A* (varargs/repeated)
|
|
381
|
+
TypeRepr.Repeated(typeA)
|
|
382
|
+
|
|
383
|
+
// A @annotation
|
|
384
|
+
TypeRepr.Annotated(typeA, List(annotation))
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Namespaces and Type Names
|
|
388
|
+
|
|
389
|
+
### Owner
|
|
390
|
+
|
|
391
|
+
`Owner` represents where a type is defined in the package hierarchy:
|
|
392
|
+
|
|
393
|
+
```scala
|
|
394
|
+
// From package path
|
|
395
|
+
val owner = Owner.fromPackagePath("com.example.app")
|
|
396
|
+
// Owner(List(Package("com"), Package("example"), Package("app")))
|
|
397
|
+
|
|
398
|
+
// Build incrementally
|
|
399
|
+
val owner = Owner.Root / "com" / "example"
|
|
400
|
+
|
|
401
|
+
// Add term (object) segment
|
|
402
|
+
val owner = (Owner.Root / "com").term("MyObject")
|
|
403
|
+
|
|
404
|
+
// Add type segment
|
|
405
|
+
val owner = (Owner.Root / "com").tpe("MyClass")
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Owner properties:
|
|
409
|
+
|
|
410
|
+
```scala
|
|
411
|
+
owner.asString // "com.example" - dot-separated path
|
|
412
|
+
owner.isRoot // true if empty
|
|
413
|
+
owner.parent // Parent owner (or Root)
|
|
414
|
+
owner.lastName // Last segment name
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Predefined Owners
|
|
418
|
+
|
|
419
|
+
TypeId provides common namespaces:
|
|
420
|
+
|
|
421
|
+
```scala
|
|
422
|
+
Owner.scala // scala
|
|
423
|
+
Owner.scalaUtil // scala.util
|
|
424
|
+
Owner.scalaCollectionImmutable // scala.collection.immutable
|
|
425
|
+
Owner.javaLang // java.lang
|
|
426
|
+
Owner.javaTime // java.time
|
|
427
|
+
Owner.javaUtil // java.util
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### TermPath
|
|
431
|
+
|
|
432
|
+
`TermPath` represents paths to term values (for singleton types):
|
|
433
|
+
|
|
434
|
+
```scala
|
|
435
|
+
// com.example.MyObject.value.type
|
|
436
|
+
val path = TermPath.fromOwner(
|
|
437
|
+
Owner.fromPackagePath("com.example").term("MyObject"),
|
|
438
|
+
"value"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
path.asString // "com.example.MyObject.value"
|
|
442
|
+
path.isEmpty // false
|
|
443
|
+
path / "nested" // Append segment
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Type Parameters
|
|
447
|
+
|
|
448
|
+
### TypeParam
|
|
449
|
+
|
|
450
|
+
Represents a type parameter specification:
|
|
451
|
+
|
|
452
|
+
```scala
|
|
453
|
+
// Basic type parameter
|
|
454
|
+
TypeParam("A", index = 0)
|
|
455
|
+
|
|
456
|
+
// Covariant (+A)
|
|
457
|
+
TypeParam("A", 0, Variance.Covariant)
|
|
458
|
+
TypeParam.covariant("A", 0)
|
|
459
|
+
|
|
460
|
+
// Contravariant (-A)
|
|
461
|
+
TypeParam("A", 0, Variance.Contravariant)
|
|
462
|
+
TypeParam.contravariant("A", 0)
|
|
463
|
+
|
|
464
|
+
// With bounds (A <: Upper)
|
|
465
|
+
TypeParam.bounded("A", 0, upper = TypeRepr.Ref(upperType))
|
|
466
|
+
|
|
467
|
+
// Higher-kinded (F[_])
|
|
468
|
+
TypeParam.higherKinded("F", 0, arity = 1)
|
|
469
|
+
TypeParam("F", 0, kind = Kind.Star1)
|
|
470
|
+
|
|
471
|
+
// Full specification
|
|
472
|
+
TypeParam(
|
|
473
|
+
name = "A",
|
|
474
|
+
index = 0,
|
|
475
|
+
variance = Variance.Covariant,
|
|
476
|
+
bounds = TypeBounds.upper(someType),
|
|
477
|
+
kind = Kind.Type
|
|
478
|
+
)
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
TypeParam properties:
|
|
482
|
+
|
|
483
|
+
```scala
|
|
484
|
+
param.name // "A"
|
|
485
|
+
param.index // Position in parameter list
|
|
486
|
+
param.variance // Covariant, Contravariant, or Invariant
|
|
487
|
+
param.bounds // TypeBounds
|
|
488
|
+
param.kind // Kind (*, * -> *, etc.)
|
|
489
|
+
|
|
490
|
+
param.isCovariant // variance == Covariant
|
|
491
|
+
param.isContravariant // variance == Contravariant
|
|
492
|
+
param.isInvariant // variance == Invariant
|
|
493
|
+
param.hasUpperBound // bounds.upper.isDefined
|
|
494
|
+
param.hasLowerBound // bounds.lower.isDefined
|
|
495
|
+
param.isProperType // kind == Kind.Type
|
|
496
|
+
param.isTypeConstructor // kind != Kind.Type
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### TypeBounds
|
|
500
|
+
|
|
501
|
+
Represents type parameter bounds:
|
|
502
|
+
|
|
503
|
+
```scala
|
|
504
|
+
// No bounds (>: Nothing <: Any)
|
|
505
|
+
TypeBounds.Unbounded
|
|
506
|
+
|
|
507
|
+
// Upper bound only (<: Upper)
|
|
508
|
+
TypeBounds.upper(upperType)
|
|
509
|
+
|
|
510
|
+
// Lower bound only (>: Lower)
|
|
511
|
+
TypeBounds.lower(lowerType)
|
|
512
|
+
|
|
513
|
+
// Both bounds (>: Lower <: Upper)
|
|
514
|
+
TypeBounds(lowerType, upperType)
|
|
515
|
+
|
|
516
|
+
// Type alias bounds (lower == upper)
|
|
517
|
+
TypeBounds.alias(aliasType)
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
TypeBounds properties:
|
|
521
|
+
|
|
522
|
+
```scala
|
|
523
|
+
bounds.lower // Option[TypeRepr]
|
|
524
|
+
bounds.upper // Option[TypeRepr]
|
|
525
|
+
bounds.isUnbounded // No bounds specified
|
|
526
|
+
bounds.hasOnlyUpper // Only upper bound
|
|
527
|
+
bounds.hasOnlyLower // Only lower bound
|
|
528
|
+
bounds.hasBothBounds // Both bounds specified
|
|
529
|
+
bounds.isAlias // lower == upper
|
|
530
|
+
bounds.aliasType // Option[TypeRepr] if alias
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Variance
|
|
534
|
+
|
|
535
|
+
```scala
|
|
536
|
+
Variance.Covariant // +A
|
|
537
|
+
Variance.Contravariant // -A
|
|
538
|
+
Variance.Invariant // A
|
|
539
|
+
|
|
540
|
+
variance.symbol // "+", "-", or ""
|
|
541
|
+
variance.isCovariant
|
|
542
|
+
variance.isContravariant
|
|
543
|
+
variance.isInvariant
|
|
544
|
+
variance.flip // Covariant <-> Contravariant
|
|
545
|
+
variance * other // Combine variances
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Kind
|
|
549
|
+
|
|
550
|
+
Represents the "kind" of a type (type of types):
|
|
551
|
+
|
|
552
|
+
```scala
|
|
553
|
+
Kind.Type // * (proper type like Int, String)
|
|
554
|
+
Kind.Star // Alias for Type
|
|
555
|
+
Kind.Star1 // * -> * (List, Option)
|
|
556
|
+
Kind.Star2 // * -> * -> * (Map, Either)
|
|
557
|
+
Kind.HigherStar1 // (* -> *) -> * (Functor, Monad)
|
|
558
|
+
|
|
559
|
+
Kind.constructor(0) // *
|
|
560
|
+
Kind.constructor(1) // * -> *
|
|
561
|
+
Kind.constructor(2) // * -> * -> *
|
|
562
|
+
|
|
563
|
+
// Custom kinds
|
|
564
|
+
Kind.Arrow(List(Kind.Type), Kind.Type) // * -> *
|
|
565
|
+
Kind.Arrow(List(Kind.Star1), Kind.Type) // (* -> *) -> *
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Kind properties:
|
|
569
|
+
|
|
570
|
+
```scala
|
|
571
|
+
kind.isProperType // kind == Kind.Type
|
|
572
|
+
kind.arity // Number of type parameters
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Members (Structural Types)
|
|
576
|
+
|
|
577
|
+
### Val/Var Members
|
|
578
|
+
|
|
579
|
+
```scala
|
|
580
|
+
// val x: Int
|
|
581
|
+
Member.Val("x", TypeRepr.Ref(TypeId.int))
|
|
582
|
+
|
|
583
|
+
// var y: String
|
|
584
|
+
Member.Val("y", TypeRepr.Ref(TypeId.string), isVar = true)
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Method Members
|
|
588
|
+
|
|
589
|
+
```scala
|
|
590
|
+
// def foo: Int
|
|
591
|
+
Member.Def("foo", Nil, Nil, TypeRepr.Ref(TypeId.int))
|
|
592
|
+
|
|
593
|
+
// def bar(x: Int): String
|
|
594
|
+
Member.Def(
|
|
595
|
+
name = "bar",
|
|
596
|
+
typeParams = Nil,
|
|
597
|
+
paramLists = List(List(Param("x", TypeRepr.Ref(TypeId.int)))),
|
|
598
|
+
result = TypeRepr.Ref(TypeId.string)
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
// def baz[A](x: A)(implicit y: Ordering[A]): List[A]
|
|
602
|
+
Member.Def(
|
|
603
|
+
name = "baz",
|
|
604
|
+
typeParams = List(TypeParam.A),
|
|
605
|
+
paramLists = List(
|
|
606
|
+
List(Param("x", TypeRepr.ParamRef(TypeParam.A))),
|
|
607
|
+
List(Param("y", orderingA, isImplicit = true))
|
|
608
|
+
),
|
|
609
|
+
result = listA
|
|
610
|
+
)
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### Type Members
|
|
614
|
+
|
|
615
|
+
```scala
|
|
616
|
+
// type T
|
|
617
|
+
Member.TypeMember("T")
|
|
618
|
+
|
|
619
|
+
// type T <: Upper
|
|
620
|
+
Member.TypeMember("T", upperBound = Some(upperType))
|
|
621
|
+
|
|
622
|
+
// type T = Alias (isAlias when lower == upper)
|
|
623
|
+
Member.TypeMember("T",
|
|
624
|
+
lowerBound = Some(aliasType),
|
|
625
|
+
upperBound = Some(aliasType)
|
|
626
|
+
)
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## TypeDefKind
|
|
630
|
+
|
|
631
|
+
Classifies what kind of type definition a TypeId represents:
|
|
632
|
+
|
|
633
|
+
### Class
|
|
634
|
+
|
|
635
|
+
```scala
|
|
636
|
+
TypeDefKind.Class(
|
|
637
|
+
isFinal = false,
|
|
638
|
+
isAbstract = false,
|
|
639
|
+
isCase = true, // case class
|
|
640
|
+
isValue = false, // extends AnyVal
|
|
641
|
+
bases = List(...) // parent types
|
|
642
|
+
)
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Trait
|
|
646
|
+
|
|
647
|
+
```scala
|
|
648
|
+
TypeDefKind.Trait(
|
|
649
|
+
isSealed = true,
|
|
650
|
+
bases = List(...)
|
|
651
|
+
)
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Object
|
|
655
|
+
|
|
656
|
+
```scala
|
|
657
|
+
TypeDefKind.Object(
|
|
658
|
+
bases = List(...)
|
|
659
|
+
)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Enum (Scala 3)
|
|
663
|
+
|
|
664
|
+
```scala
|
|
665
|
+
TypeDefKind.Enum(
|
|
666
|
+
bases = List(...)
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
TypeDefKind.EnumCase(
|
|
670
|
+
parentEnum = parentEnumRef,
|
|
671
|
+
ordinal = 0,
|
|
672
|
+
isObjectCase = true
|
|
673
|
+
)
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Type Aliases and Opaque Types
|
|
677
|
+
|
|
678
|
+
```scala
|
|
679
|
+
TypeDefKind.TypeAlias // type Foo = Bar
|
|
680
|
+
|
|
681
|
+
TypeDefKind.OpaqueType(
|
|
682
|
+
publicBounds = TypeBounds.Unbounded // Bounds visible outside
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
TypeDefKind.AbstractType // Abstract type member
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
## Annotations
|
|
689
|
+
|
|
690
|
+
Represent Scala/Java annotations attached to types:
|
|
691
|
+
|
|
692
|
+
```scala
|
|
693
|
+
Annotation(
|
|
694
|
+
typeId = TypeId.of[deprecated],
|
|
695
|
+
args = List(
|
|
696
|
+
AnnotationArg.Named("message",
|
|
697
|
+
AnnotationArg.Const("use newMethod")),
|
|
698
|
+
AnnotationArg.Named("since",
|
|
699
|
+
AnnotationArg.Const("1.0"))
|
|
700
|
+
)
|
|
701
|
+
)
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
Annotation argument types:
|
|
705
|
+
|
|
706
|
+
```scala
|
|
707
|
+
AnnotationArg.Const(value) // Constant value
|
|
708
|
+
AnnotationArg.ArrayArg(values) // Array of args
|
|
709
|
+
AnnotationArg.Named(name, value) // Named parameter
|
|
710
|
+
AnnotationArg.Nested(annotation) // Nested annotation
|
|
711
|
+
AnnotationArg.ClassOf(typeRepr) // classOf[T]
|
|
712
|
+
AnnotationArg.EnumValue(enumType, valueName) // Enum constant
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
## Predefined TypeIds
|
|
716
|
+
|
|
717
|
+
TypeId provides instances for common types:
|
|
718
|
+
|
|
719
|
+
### Primitives
|
|
720
|
+
|
|
721
|
+
```scala
|
|
722
|
+
TypeId.unit // scala.Unit
|
|
723
|
+
TypeId.boolean // scala.Boolean
|
|
724
|
+
TypeId.byte // scala.Byte
|
|
725
|
+
TypeId.short // scala.Short
|
|
726
|
+
TypeId.int // scala.Int
|
|
727
|
+
TypeId.long // scala.Long
|
|
728
|
+
TypeId.float // scala.Float
|
|
729
|
+
TypeId.double // scala.Double
|
|
730
|
+
TypeId.char // scala.Char
|
|
731
|
+
TypeId.string // java.lang.String
|
|
732
|
+
TypeId.bigInt // scala.BigInt
|
|
733
|
+
TypeId.bigDecimal // scala.BigDecimal
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Collections
|
|
737
|
+
|
|
738
|
+
```scala
|
|
739
|
+
TypeId.option // scala.Option
|
|
740
|
+
TypeId.some // scala.Some
|
|
741
|
+
TypeId.none // scala.None
|
|
742
|
+
TypeId.list // scala.collection.immutable.List
|
|
743
|
+
TypeId.vector // scala.collection.immutable.Vector
|
|
744
|
+
TypeId.set // scala.collection.immutable.Set
|
|
745
|
+
TypeId.seq // scala.collection.immutable.Seq
|
|
746
|
+
TypeId.indexedSeq // scala.collection.immutable.IndexedSeq
|
|
747
|
+
TypeId.map // scala.collection.immutable.Map
|
|
748
|
+
TypeId.either // scala.util.Either
|
|
749
|
+
TypeId.array // scala.Array
|
|
750
|
+
TypeId.arraySeq // scala.collection.immutable.ArraySeq
|
|
751
|
+
TypeId.chunk // zio.blocks.chunk.Chunk
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### java.time Types
|
|
755
|
+
|
|
756
|
+
```scala
|
|
757
|
+
TypeId.dayOfWeek // java.time.DayOfWeek
|
|
758
|
+
TypeId.duration // java.time.Duration
|
|
759
|
+
TypeId.instant // java.time.Instant
|
|
760
|
+
TypeId.localDate // java.time.LocalDate
|
|
761
|
+
TypeId.localDateTime // java.time.LocalDateTime
|
|
762
|
+
TypeId.localTime // java.time.LocalTime
|
|
763
|
+
TypeId.month // java.time.Month
|
|
764
|
+
TypeId.monthDay // java.time.MonthDay
|
|
765
|
+
TypeId.offsetDateTime // java.time.OffsetDateTime
|
|
766
|
+
TypeId.offsetTime // java.time.OffsetTime
|
|
767
|
+
TypeId.period // java.time.Period
|
|
768
|
+
TypeId.year // java.time.Year
|
|
769
|
+
TypeId.yearMonth // java.time.YearMonth
|
|
770
|
+
TypeId.zoneId // java.time.ZoneId
|
|
771
|
+
TypeId.zoneOffset // java.time.ZoneOffset
|
|
772
|
+
TypeId.zonedDateTime // java.time.ZonedDateTime
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### java.util Types
|
|
776
|
+
|
|
777
|
+
```scala
|
|
778
|
+
TypeId.currency // java.util.Currency
|
|
779
|
+
TypeId.uuid // java.util.UUID
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
## Integration with Schema
|
|
783
|
+
|
|
784
|
+
TypeId is central to ZIO Blocks' schema system. Every `Reflect` node has an associated TypeId:
|
|
785
|
+
|
|
786
|
+
```scala
|
|
787
|
+
import zio.blocks.schema._
|
|
788
|
+
|
|
789
|
+
case class Person(name: String, age: Int)
|
|
790
|
+
object Person {
|
|
791
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Access TypeId from schema
|
|
795
|
+
val reflect = Schema[Person].reflect
|
|
796
|
+
val typeId = reflect.typeId
|
|
797
|
+
|
|
798
|
+
typeId.name // "Person"
|
|
799
|
+
typeId.isCaseClass // true
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
### Schema Transformations
|
|
803
|
+
|
|
804
|
+
TypeId is captured when transforming schemas:
|
|
805
|
+
|
|
806
|
+
```scala
|
|
807
|
+
case class Email(value: String)
|
|
808
|
+
|
|
809
|
+
object Email {
|
|
810
|
+
implicit val schema: Schema[Email] = Schema[String]
|
|
811
|
+
.transform(Email(_), _.value)
|
|
812
|
+
.withTypeName[Email] // Sets TypeId to Email
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### Schema Derivation
|
|
817
|
+
|
|
818
|
+
The `Deriver` trait receives TypeId for each node:
|
|
819
|
+
|
|
820
|
+
```scala
|
|
821
|
+
trait Deriver[TC[_]] {
|
|
822
|
+
def deriveRecord[A](
|
|
823
|
+
typeId: TypeId[A],
|
|
824
|
+
fields: => Chunk[Deriver.Field[TC, A, _]],
|
|
825
|
+
...
|
|
826
|
+
): TC[A]
|
|
827
|
+
|
|
828
|
+
def deriveVariant[A](
|
|
829
|
+
typeId: TypeId[A],
|
|
830
|
+
cases: => Chunk[Deriver.Case[TC, A, _]],
|
|
831
|
+
...
|
|
832
|
+
): TC[A]
|
|
833
|
+
|
|
834
|
+
// ... other methods
|
|
835
|
+
}
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
## Type Normalization
|
|
839
|
+
|
|
840
|
+
Type aliases are normalized to their underlying types for comparison:
|
|
841
|
+
|
|
842
|
+
```scala
|
|
843
|
+
type Age = Int
|
|
844
|
+
|
|
845
|
+
val ageId = TypeId.alias[Age]("Age", owner, Nil, TypeRepr.Ref(TypeId.int))
|
|
846
|
+
val normalized = TypeId.normalize(ageId)
|
|
847
|
+
|
|
848
|
+
normalized.fullName // "scala.Int" (not "Age")
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
Normalization handles nested aliases and type arguments:
|
|
852
|
+
|
|
853
|
+
```scala
|
|
854
|
+
type IntList = List[Int]
|
|
855
|
+
type MyIntList = IntList
|
|
856
|
+
|
|
857
|
+
// Normalizing MyIntList resolves through IntList to List[Int]
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
## Equality and Hashing
|
|
861
|
+
|
|
862
|
+
TypeId uses structural equality that accounts for type aliases:
|
|
863
|
+
|
|
864
|
+
```scala
|
|
865
|
+
val alias1 = TypeId.alias[A]("A", owner, Nil, TypeRepr.Ref(TypeId.int))
|
|
866
|
+
val alias2 = TypeId.alias[A]("A", owner, Nil, TypeRepr.Ref(TypeId.int))
|
|
867
|
+
|
|
868
|
+
alias1 == alias2 // true (structural equality)
|
|
869
|
+
|
|
870
|
+
// Works correctly in hash maps
|
|
871
|
+
val map = Map(alias1 -> "value")
|
|
872
|
+
map(alias2) // "value"
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
## Erased TypeId
|
|
876
|
+
|
|
877
|
+
For type-indexed collections where the type parameter doesn't matter:
|
|
878
|
+
|
|
879
|
+
```scala
|
|
880
|
+
// TypeId.Erased is TypeId[TypeId.Unknown]
|
|
881
|
+
val erased: TypeId.Erased = typeId.erased
|
|
882
|
+
|
|
883
|
+
// Use in maps keyed by type
|
|
884
|
+
val typeRegistry: Map[TypeId.Erased, Schema[_]] = Map(
|
|
885
|
+
TypeId.int.erased -> Schema[Int],
|
|
886
|
+
TypeId.string.erased -> Schema[String]
|
|
887
|
+
)
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
## Runtime Reflection
|
|
891
|
+
|
|
892
|
+
On JVM, TypeId can retrieve the corresponding `Class`:
|
|
893
|
+
|
|
894
|
+
```scala
|
|
895
|
+
val typeId = TypeId.of[Person]
|
|
896
|
+
val clazz: Option[Class[_]] = typeId.clazz
|
|
897
|
+
|
|
898
|
+
// Construct instances (JVM only)
|
|
899
|
+
val result: Either[String, Any] = typeId.construct(Chunk("Alice", 30))
|
|
900
|
+
```
|