Skip to main content

Overview

The GLYPH type system provides formal definitions for all supported data types, including primitive types, collection types, and user-defined structures. This specification defines the type grammar, constraint system, and schema language.

Primitive Types

Core Types

TypeDescriptionWire FormatExamples
strUTF-8 stringBare or quotedhello, "hello world"
intSigned 64-bit integerDecimal42, -100, 0
floatIEEE 754 doubleShortest roundtrip3.14, 1e-06, 1e+15
boolBooleant / ft, f
nullNull value_ or _

Extended Types

TypeDescriptionWire FormatExamples
bytesBinary dataBase64-encodedb64"SGVsbG8="
timeTimestampISO-86012025-01-13T12:00:00Z
idTyped referencePrefix + value^user:abc123, ^org:acme
blobLarge binary referenceContent-addressedblob:sha256:abc123...

Type Properties

Integer (int):
  • Range: -2^63 to 2^63-1
  • No leading zeros (except 0)
  • Decimal representation only (no hex/octal)
Float (float):
  • IEEE 754 double precision
  • Exponent notation for exp < -4 or exp >= 15
  • NaN and Infinity rejected
  • Negative zero canonicalizes to 0
String (str):
  • Valid UTF-8 required
  • Bare-safe strings unquoted
  • Quoted strings use minimal escapes
  • No maximum length (implementation limits may apply)
Timestamp (time):
  • ISO-8601 format with optional timezone
  • Microsecond precision
  • Canonical form uses Z for UTC
ID (id):
  • Format: ^prefix:value
  • Prefix: 1-12 lowercase letters
  • Value: alphanumeric + - + _
  • Examples: ^user:123, ^org:acme-corp

Collection Types

List

Syntax: [ + space-separated elements + ] Properties:
  • Ordered sequence
  • Heterogeneous (elements can have different types)
  • Zero-indexed
Examples:
[]                    # Empty list
[1 2 3]              # Homogeneous
[_ t 42 hello]       # Heterogeneous
[[1 2] [3 4]]        # Nested

Map

Syntax: { + sorted key=value pairs + } Properties:
  • Unordered key-value pairs
  • Keys must be strings
  • Keys sorted by UTF-8 byte order in canonical form
  • Values can be any type
Examples:
{}                           # Empty map
{a=1}                       # Single entry
{a=1 b=2 c=3}              # Multiple entries
{name=Alice age=30}        # Mixed values
{nested={x=1 y=2}}         # Nested maps

Struct

Syntax: TypeName{field=value ...} or TypeName:version{...} Properties:
  • Named type with versioning
  • Defined fields with types
  • Can be open (accepts unknown fields) or closed
Examples:
User{name=Alice age=30}
User:v2{name=Alice age=30 [email protected]}
Point{x=1.5 y=2.3}

Type Constraints

Integer Constraints

Syntax: int<min,max> Examples:
port int<1024,65535>        # Valid port number
limit int<1,100>            # Pagination limit
age int<0,150>              # Human age

String Constraints

Syntax: str<minlen,maxlen> Examples:
query str<1,500>            # Non-empty, max 500 chars
username str<3,20>          # Username length
password str<8,128>         # Password requirements
Pattern Constraints:
email str pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
slug str pattern="^[a-z0-9-]+$"

Enum Constraints

Syntax: enum[value1,value2,...] Examples:
status enum[pending,active,complete,failed]
units enum[celsius,fahrenheit,kelvin]
role enum[admin,editor,viewer]

List Constraints

Syntax: list<type, minlen, maxlen> Examples:
tags list<str, 0, 10>       # Up to 10 string tags
scores list<int, 1, 100>    # At least 1 score

Map Constraints

Syntax: map<keytype, valuetype> Examples:
metadata map<str, str>              # String to string
scores map<str, int>                # String keys, int values
config map<str, Config>             # String keys, struct values

Schema Language

Schema Definition

Syntax:
TypeName:version [annotation] struct {
    fieldName: type [constraints]
    ...
}
Annotations:
  • @open - Accept unknown fields
  • @packed - Use compact encoding
  • @deprecated - Mark as deprecated

Example Schema

User:v1 struct {
    id: id
    name: str<1,100>
    email: str pattern="^[^@]+@[^@]+$"
    age: int<0,150>
    roles: list<enum[admin,editor,viewer], 1, 10>
    metadata: map<str, str>
    created: time
}

Config:v1 @open struct {
    name: str<1,50>
    port: int<1024,65535>
    settings: map<str, int>
}

SearchRequest:v1 struct {
    query: str<1,500>
    max_results: int<1,100> = 10
    filters: map<str, str>
}

Field Definitions

Required fields:
name: str               # Must be present
Optional fields:
description: str?       # May be null or absent
Default values:
max_results: int = 10   # Uses 10 if not provided
active: bool = t        # Defaults to true

Type References

Primitive references:
field: str
field: int
field: bool
Struct references:
address: Address        # Reference to Address struct
owner: User:v2          # Reference to specific version
Nested references:
metadata: map<str, Metadata>
users: list<User>
config: Config?

Validation

Type Validation

Type validation ensures values match their declared types:
// Define schema
schema := NewSchemaBuilder().
    AddStruct("User", "v1",
        Field("name", PrimitiveType("str")),
        Field("age", PrimitiveType("int")),
    ).
    Build()

// Validate value
value := Struct("User", "v1",
    Field("name", Str("Alice")),
    Field("age", Int(30)),
)

result := schema.Validate(value)
if !result.Valid {
    for _, err := range result.Errors {
        log.Println(err)
    }
}

Constraint Validation

Constraints are checked during validation: Integer bounds:
age: int<0,150>

# Valid: 30
# Invalid: -5, 200
String length:
username: str<3,20>

# Valid: "alice", "bob123"
# Invalid: "ab", "verylongusernamethatexceedslimit"
Enum membership:
status: enum[pending,active,complete]

# Valid: pending, active, complete
# Invalid: running, done

Strict vs Loose Validation

Loose validation:
  • Unknown fields in @open structs: warning
  • Missing optional fields: allowed
  • Type coercion: allowed (e.g., int to float)
Strict validation:
  • Unknown fields: error (even in @open structs)
  • All declared fields must be present
  • No type coercion
// Loose validation
result := ValidateWithSchema(value, schema)

// Strict validation
result := ValidateStrict(value, schema)

Schema Evolution

Versioning

Schemas support versioning for backward compatibility:
User:v1 struct {
    name: str
    age: int
}

User:v2 struct {
    name: str
    age: int
    email: str?          # New optional field
}

Compatibility Rules

Backward compatible changes:
  • Add optional fields
  • Add default values
  • Make required field optional
  • Expand enum values
  • Relax constraints (widen ranges)
Breaking changes:
  • Remove fields
  • Change field types
  • Add required fields
  • Remove enum values
  • Tighten constraints

Migration Strategies

Version negotiation:
# Client sends supported versions
Request{versions=[v1 v2 v3]}

# Server responds with chosen version
Response{version=v2 data=User:v2{...}}
Graceful degradation:
// Try latest version first
value, err := ParseAs(data, "User:v3")
if err != nil {
    // Fall back to v2
    value, err = ParseAs(data, "User:v2")
}

Type System Grammar

BNF Definition

type        ::= primitive | collection | reference | constrained
primitive   ::= "str" | "int" | "float" | "bool" | "null" | "bytes" | "time" | "id"
collection  ::= "list" | "map" | "struct"
reference   ::= TypeName [":" version]
constrained ::= type "<" constraints ">"

constraints ::= intConstraints | strConstraints | enumConstraints | collConstraints
intConstraints ::= min "," max
strConstraints ::= minlen "," maxlen | "pattern=" pattern
enumConstraints ::= value ("," value)*
collConstraints ::= type "," minlen "," maxlen

schema      ::= TypeName ":" version annotation* "struct" "{" field* "}"
field       ::= name ":" type ("?" | "=" default)?
annotation  ::= "@" name

Conformance

Implementation Requirements

A conformant type system implementation MUST:
  1. Support all primitive types
  2. Support all collection types
  3. Enforce type constraints during validation
  4. Support schema versioning
  5. Provide strict and loose validation modes
  6. Handle unknown fields according to @open annotation
  7. Validate nested types recursively
  8. Report validation errors with field paths

Cross-Language Compatibility

All implementations MUST:
  • Accept identical valid inputs
  • Reject identical invalid inputs
  • Report compatible error messages
  • Support the same constraint syntax

Build docs developers (and LLMs) love