Invisible link to canonical for Microformats

ADR-036 Unified copy Semantics for Structs and Maps


Status

Accepted

Context

Slug supports immutable data structures and favors structural updates over mutation.

Structs already support immutable update semantics through the copy keyword:

val next = user copy {
  age: 43
}

However, maps currently rely on lower-level update functions such as:

val next = put(config, :timeout, 5000)

This creates an inconsistency in how immutable updates are expressed across structurally similar data types.

The inconsistency becomes more pronounced in a dynamically typed language where values are frequently data-shaped and may originate from:

  • JSON payloads
  • ACP messages
  • configuration maps
  • structs
  • module exports
  • runtime metadata

This distinction is also difficult for AI-assisted code generation systems, which strongly benefit from uniform transformation semantics.

Decision

The copy keyword SHALL support both structs and maps.

copy represents a type-preserving immutable structural update operation.

Examples:

val nextUser = user copy {
  age: 43
}

val nextConfig = config copy {
  timeout: 5000
}

Struct Semantics

When used with structs, copy SHALL:

  • preserve the original struct type
  • validate updated fields
  • reject unknown fields
  • return a new struct instance

Example:

val next = user copy {
  age: 43
}

Map Semantics

When used with maps, copy SHALL:

  • preserve the map type
  • apply key updates as a shallow structural merge
  • allow arbitrary keys
  • return a new map instance

Example:

val next = config copy {
  timeout: 5000,
  retries: 3
}

Shallow Copying

copy SHALL always perform shallow structural updates.

Nested values are reused unless explicitly replaced.

Example:

val next = state copy {
  user: state.user copy {
    active: true
  }
}

Unsupported Types

copy is not supported for primitive scalar values.

Attempting to use copy with unsupported types SHALL produce a runtime error.

Example:

42 copy {
  value: 99
}

Design Philosophy

copy expresses semantic intent:

  • derive a new value from an existing value
  • preserve immutability
  • minimize representational variance across data types

This aligns with Slug’s broader language goals:

  • explicit immutable state transitions
  • structurally readable code
  • minimal special cases
  • AI-friendly semantics

Consequences

Positive

  • Unifies immutable update semantics across structs and maps
  • Reduces cognitive overhead in dynamically typed code
  • Improves readability by emphasizing transformation intent over update mechanics
  • Reduces representational variance for AI-assisted code generation
  • Reinforces immutable programming patterns
  • Keeps structural updates visually consistent across the language
  • Avoids introducing additional update keywords or merge syntax

Negative

  • copy semantics differ slightly between structs and maps
  • Runtime type validation remains necessary for struct updates
  • Some users may initially expect deep copy behavior

Neutral

  • put() and related map functions remain valid lower-level primitives
  • copy continues to represent shallow structural updates only
  • Future composite types may optionally adopt copy semantics