Zax identity types
| Field | Value |
|---|---|
| Status | Current conceptual design |
| Audience | Human developers defining, reading, or converting aliases and representation-related types |
| Applies To | Transparent type aliases and distinct identities over existing types; not a formal grammar or specification |
| Implementation State | Not established by this repository |
| Owns | Transparent aliases and canonical identity preservation under alias property overlays; identity boundaries; immediate underlying type/value/place operations; identity declarations and original-owner body authority; admission; identity projection; exposed and opaque surfaces; identity-specific application of the shared composition exposure filter; contextual-posture reset and non-forwarding; declared bridges; construction/transfer requirements; costs, diagnostics, and source stability |
| Does Not Own | Integer-family membership and numeric-source realization (integers, integer literals and realization); enum members and policies (enums); qualifier semantics (qualifiers); the shared exposure filter and general composition behavior (Zax composition); partial behavior (partials); or general structural shape and compatibility |
| Source / Provenance | Legacy alias and enum evidence refined through fundamental-integer and conversion review |
Two ways to build on an existing type
Zax can give an existing type another name, or define a new type that uses an existing type as its underlying type.
MyShortName :: alias type SomeLongTypeName
MyCount :: identity admit expose type U32
MyShortName and SomeLongTypeName are the same type. The alias only provides
another name.
MyCount and U32 are different types. MyCount uses U32 as its underlying
type, so it begins with the same representation and value capacity. The
identity declaration decides how U32 values may create MyCount values and
whether U32 functions and operators become available on MyCount.
myRaw : U32 = 10
myCount : MyCount = MyCount from myRaw
myRaw = myCount // error: different identities
The explicit from states where the program crosses between them.
Enums specialize this model. An enum is an integer-backed identity that adds known member values and strict, relaxed, flags, string, and generated-operation policies defined by Zax enums.
An enum may use a language-provided integer identity such as Integer as its
immediate backing:
MyEnum :: enum {
}
Backing :: alias type MyEnum underlying type // Integer
The enum-to-Integer boundary and the deeper Integer-to-exact-intrinsic
boundary remain separate. Immediate underlying operations cross only one
boundary.
Transparent aliases
MyShortName :: alias type SomeLongTypeName
A transparent alias is another name for one existing type:
- both names refer to the same type identity;
- no conversion is required between the names;
- both names have the same values, operations, and representation;
- a use may carry an explicitly overlaid qualification, stance, indirection, or compatibility profile without changing canonical identity; and
- the alias creates no additional overload domain.
myOriginal : SomeLongTypeName
myAlias : MyShortName = myOriginal
Diagnostics may retain the alias spelling when that helps explain source:
MyShortName (alias of SomeLongTypeName)
That presentation does not make the alias another type.
Alias property overlays do not create identity
A concrete alias may abbreviate the complete property profile accepted where a type is explicitly declared:
DeepView :: alias type MyType readonly & deep
CopyView :: alias type DeepView writable copy
Both aliases retain the canonical MyType identity. CopyView inherits the
reference shape, replaces the access and transfer axes it states, and receives
ordinary defaults only for still-unresolved properties.
The overlay does not convert or modify an existing value. A declaration using
CopyView still fails when its source cannot provide writable access or the
requested transfer. Alias expansion never manufactures capability.
The overlay algorithm and exact alias categories are defined by declarations and bindings. Qualifiers, transfer stances, and structural compatibility retain the meaning and safety constraints of their axes.
Identity boundary and underlying type
An identity boundary separates types that are not interchangeable even when their representation is the same.
Assignment, argument passing, return, and operator matching do not cross the boundary unless the language or type declares an explicit operation:
myCount : MyCount
myRaw : U32
myCount = myRaw // error: identity boundary
myCount = MyCount from myRaw
The identity type is the new outer type, such as MyCount.
The underlying type is the existing type on which it is based, such as
U32.
The underlying value is the value represented before admission or returned after identity projection.
An identity boundary is semantic, not necessarily expensive. Crossing it may require no representation change while still requiring explicit source.
Identity declaration
An identity declaration answers two independent questions:
- May every valid underlying value create this identity automatically?
- Which functions and operators from the underlying type become available?
Both answers are explicit:
Name :: identity <admission> <surface> type UnderlyingType
Exactly one admission keyword is written:
| Keyword | Meaning |
|---|---|
admit |
Permit ordinary from for every valid underlying value when the requested construction or transfer is viable |
restricted |
No ordinary admission is generated; the identity declares validation, unchecked admission, unsafe admission, or no admission |
Exactly one surface keyword is written:
| Keyword | Meaning |
|---|---|
expose |
Make eligible underlying functions and operators available after adapting them to the new identity |
opaque |
Do not make underlying functions or operators available automatically |
The four combinations serve different purposes:
MyInteger :: identity admit expose type Integer
MyValidatedInteger :: identity restricted expose type Integer
MyToken :: identity admit opaque type U64
MyDocumentHandle :: identity restricted opaque type Integer
There is no omission default for either choice. Admission and exposure state consequential intent.
Original definition body and completion
An identity without a body completes as soon as its declaration finishes:
MyInteger :: identity admit expose type Integer
An original definition body may keep the identity open while its owner adds constructors, functions, and operators:
// Illustrative future identity-body/completion syntax.
MyContextualInteger :: identity admit expose type Integer {
+++ contextual final : ()(rhs : Integer) = {
}
operator binary '+' contextual existing
double final : ()() = {
_ *= 2
}
}
This is a compile-time definition phase, not a runtime state. No code observes a partially defined identity. The closing brace completes the identity’s own definition. Later additions come only from partials, as described in partial definitions.
The body belongs to the original identity owner. It does not introduce class inheritance: the identity remains a distinct representation/composition-based type with the admission and exposure choices stated by its header.
A new identity resets inherited completion posture to explicit. The owner
must opt its construction and exposed operator into contextual completion
separately:
myValue := 0 + (: MyContextualInteger = 5)
Exact identity-body and contextual existing grammar remains future source
integration. Completion behavior is defined by
Zax operators.
Integer underlying types do not grant integer membership
An identity does not become an intrinsic integer merely because its underlying type is an integer:
MyInteger :: identity admit expose type Integer
myValue := 0 + (: MyInteger = 5)
// error by default: identity construction and exposed + are explicit
admit supplies declared admission and expose adapts eligible operations to
the new identity. Neither grants fundamental integer realization or general
implicit admission from numeric source. Contextual completion is an independent
opt-in owned by the identity declaration.
A transparent alias remains the underlying integer type and retains its behavior. A future custom numeric-family mechanism may define genuine programmer-supplied integer participation; ordinary identity declarations do not. See Zax integer literals and realization.
Admission
Admission creates a value of an identity type from a value of its underlying type. A type family may also declare admission from another explicitly related type.
Ordinary admission
An admit identity supplies ordinary from for its underlying type:
myRaw : U32 = 10
myCount : MyCount = MyCount from myRaw
Every valid U32 value is a valid underlying value for MyCount, so no
semantic validation is required.
Type-specific families may declare other ordinary admissions. For example, integer role identities may accept values from another integer type when its complete value range fits. Those relationships belong to the type family that defines them.
Restricted admission
A restricted identity receives no automatic ordinary from:
myHandle : MyDocumentHandle =
MyDocumentHandle from myRaw // error unless MyDocumentHandle declares it
The identity may instead declare one or more policies:
myHandle : MyDocumentHandle? =
MyDocumentHandle optional from myRaw
myUnchecked :=
MyDocumentHandle unchecked from myRaw
myUnsafe :=
MyCapability unsafe from myBits
optional fromvalidates and returns absent when the value is rejected.unchecked fromskips semantic validation but retains defined mechanical behavior.unsafe frombypasses an invariant required for memory, lifetime, representation, concurrency, or optimizer correctness.
restricted does not decide which of these exists. The identity’s invariants
decide whether admission is validated, unchecked, unsafe, or unavailable.
The resulting wrapper’s construction, qualification, and lifetime behavior is defined by Zax optional values.
Complete safe-subset classification remains future safety work.
Identity projection
Identity projection takes an identity value and returns its underlying value without changing the represented value:
myCount : MyCount
myRaw := myCount as U32
myRaw is the U32 value represented by myCount. Projection changes the
static type from MyCount to its underlying U32; it does not make another
identity valid.
An opaque identity receives this immediate projection without receiving every operation of its underlying type.
Type-specific exposed identities may provide additional conversions. Integer identity conversion is taught by Zax integers.
Direct bridges between identities
Two identity types defined independently from the same underlying type remain unrelated:
MyDocumentHandle :: identity restricted opaque type Integer
MyFileHandle :: identity restricted opaque type Integer
myDocument : MyDocumentHandle
myFile : MyFileHandle = myDocument // error
The general route is explicit projection followed by admission:
myFile :=
MyFileHandle unchecked from (myDocument as Integer)
A direct bridge exists only when the type family declares that relationship. Representation equality alone is insufficient.
For example, integer size and index identities may deliberately declare exact bridges because their ranges and representations are equal:
mySize : TypeSize = myIndex as TypeSize
myIndex : IndexSize = mySize as IndexSize
Exposed and opaque behavior
expose makes eligible functions and operators of the underlying type
available on the identity type. The simplest case is an identity whose complete
representation is exactly its underlying value:
Integer + Integer -> Integer
For a representation-trivial MyInteger, exposure can adapt that operation
conceptually to:
MyInteger + MyInteger -> MyInteger
No missing state has to be invented: a complete MyInteger consists of the
underlying integer at offset zero. Mechanically compatible by-copy inputs and a
copied underlying-self result can therefore substitute the identity type.
Results belonging to another domain stay in that domain. An integer comparison
still returns Boolean; a shift count still uses the integer’s associated count
type.
Additional resident data changes the answer. Exact identity-body grammar remains future source integration, but consider an identity that conceptually stores a tag as well:
// Illustrative future identity-body syntax.
TaggedInteger :: identity admit expose type Integer {
tag : Tag
}
A copied Integer result is not a complete TaggedInteger: no mechanical rule
can choose the missing tag. It may nevertheless pass through unchanged as an
Integer; only remapping it to TaggedInteger is unavailable:
TaggedInteger + Integer -> Integer
Exposure may also retain operations whose results already belong to another
domain, such as a comparison returning Boolean, when all inputs map safely.
Only an offset-zero, representation-trivial identity has the narrower exception
shown above in which a copied underlying-self result can substitute the complete
identity type.
Reference and pointer results have a different concern. They can map from the
underlying receiver back to the identity receiver only when an explicit,
verified self contract proves that the returned reference or pointer is the
receiver. Exposure never guesses result origin.
These rules use the same
mechanical eligibility filter as
ordinary composition. An identity with additional data receives only the subset
that an analogous expose member container could safely expose. Exposure checks
the mechanical mapping; it does not prove user-defined cache or cross-member
invariants. The identity owner may fence a generated signature or write a
custom wrapper.
The additional-data rule constrains future identity-body syntax; the illustrative body form above is not itself settled.
This is a programmer model, not required compiler lowering and not class inheritance.
opaque performs none of this automatic exposure. The identity may still
declare its own functions and operators.
Immediate underlying operations do not forward
The original definition body normally uses the adapted identity surface:
double final : ()() = {
_ *= 2
}
When owner code needs the underlying relationship directly, each identity boundary provides operations for exactly its immediate layer:
UnderlyingType :: alias type MyIdentity underlying type
myUnderlying := myIdentity underlying value
underlying type returns the immediate underlying type. underlying value
returns one copied/projected value of that type. Existing
as UnderlyingType projection remains the explicit conversion-shaped route to
the same immediate underlying value.
underlying place is generated as a private final post-unary operation. It is
eligible only inside the private context implementing this immediate identity
boundary and grants no stronger place, value, access, or lifetime permission
than the receiver path already carries:
doubleUnderlying final : ()() = {
(_ underlying place) *= 2
}
The operation produces a place, not a first-class reference with independent alias or escape guarantees. A function may bind a reference from that place when ordinary permissions allow it.
underlying type, underlying value, and underlying place describe the
current boundary and are not mechanically forwarded by expose. An outer
identity regenerates them for its own immediate layer. It may obtain a copied
deeper value without gaining the inner identity’s private place access:
myDeeperValue := (_ underlying place) underlying value
myDeeperValue.doSomething()
The first operation reaches the outer identity’s immediate underlying place.
The second invokes the inner value’s public copied/projected operation. Changes
to myDeeperValue do not mutate the nested stored value.
A second underlying place is unavailable unless the current code is also
private-eligible for that inner identity or the inner owner deliberately
publishes another access operation. Wrapping a private identity does not grant
permission to tunnel through it.
Optional naming of the underlying place remains future identity design.
own-published member paths preserve the physical place’s qualification and
lifetime under
current composition rules.
Construction and transfer
By-value admission requests a new identity value:
myIdentity := MyIdentity from myUnderlying
The operation needs a viable way to establish the underlying stored value:
copy;deepcopy;move;- consuming/
lasttransfer; - direct construction; or
- another declared transfer.
Identity syntax does not manufacture copyability. If no applicable construction
or transfer exists, by-value admission is unavailable. move may fall back to
copy; last may fall back to move and then copy; deep requires an exact deep
contract. Complete stance meaning and source state are defined by
Zax transfer stances.
A same-storage identity reference is a different operation. It would create a view of existing storage rather than a new value, perform no copy, and schedule no independent destruction. It constructs another reference bound permanently to the same instance place and forwards the underlying origin and lifetime requirements. Identity syntax cannot hide replacement, move, or destruction of that place.
Exact identity-reference admission, representation-cast, alias, qualifier, and qualified type-identity discovery remain future identity and casting work. Complete reference behavior is defined by lifetimes and references.
The compiler never silently substitutes a reference view for a requested by-value result.
Representation and qualifications
An identity begins with its underlying value at byte offset zero and adds no instance storage merely because it has another identity. An identity definition may carry additional resident data; that data affects total size, alignment, copy behavior, and exposure eligibility. Complete layout, shape, and same-storage-reference behavior follows Zax structural shapes and compatibility.
A representation-trivial identity retains the scalar format or aggregate shape
of its immediate underlying value while adding a distinct outer identity.
Equal shape never creates implicit admission or projection. A compatibility
posture or explicit structural operation may authorize a recast independently
from the identity’s admit/restricted and expose/opaque policies.
A whole-root same-storage compatible reference can retain varying replacement authority only when complete resident-lifetime, qualification, tracking, construction, replacement, and destruction obligations match. Additional resident identity data or incompatible custom lifecycle behavior prevents that relationship.
Identity does not weaken qualifications. Projection, admission, construction,
and transfer use the applicable place, value, access, copy, deep, move, last,
and lifetime rules.
Storage compatibility never grants implicit conversion.
Nothing preparation
A representation-trivial identity uses its compiler-prepared underlying default when a Nothing instance needs a readable member:
MyCount :: identity admit expose type U32
The prepared MyCount representation starts from the prepared U32 default.
This is not ordinary source admission through from and does not bypass an
identity boundary for ordinary values.
An identity with additional resident data follows the recursive aggregate
member rules. If an exact member path has no valid prepared representation,
access through compiler-provided Nothing uses the applicable panic or hardware
trap where supported. A trapping policy requests target trapping before
exposing the identity, while custom Nothing storage is prepared by its
+++ final once body.
Complete behavior belongs to Zax Nothing instances.
Partial definitions
An identity is a type, so a partial can add functions to it unless its declaration closes callable additions:
MyIntegerExtension :: partial MyInteger {
// Additional MyInteger functions.
}
A partial adds; it does not suppress, hide, restrict, remove, or replace behavior exposed by the original identity declaration, and it is visible only where a scope grants it.
Like any type, an identity accepts added stored members only when it writes
seal open storage. Stored members change its representation, and so the
exposure mappings and bridges that depend on it. Partials supplied by the
language, the compiler, or a CPU provider remain future work.
Costs and diagnostics
An identity may share representation with its underlying type while still introducing meaningful work or constraints:
- explicit construction or transfer;
- validation and optional failure;
- an unchecked or unsafe assertion;
- copies, moves, references, and temporaries;
- another overload domain; and
- exposed or locally declared operations.
Diagnostics should distinguish:
- transparent alias from distinct identity;
- the identity type from its underlying type;
- missing ordinary admission from failed validation;
- optional, unchecked, and unsafe admission;
- immediate identity projection from unavailable identity-to-identity conversion;
- equal representation without a declared bridge;
- an operation absent because the identity is opaque; and
- unavailable by-value admission because no construction or transfer applies.
Diagnostics lead with source/public names and make underlying representation detail available on demand.
Source stability
- Changing a transparent alias changes the type denoted by that name.
- Changing one overlaid qualifier, stance, indirection, or compatibility property changes which declarations and sources can use that alias without creating another canonical identity.
- Changing an identity’s underlying type may change layout, value capacity, and cost without erasing its identity boundary.
- Changing
admit/restrictedorexpose/opaquechanges source validity. - Adding a direct bridge enables previously invalid identity conversion.
- Changing underlying behavior may affect an exposed identity under the current shared composition exposure filter.
- Adding resident identity data may make mappings that require a copied identity result ineligible.
These are compatibility events, not invisible implementation choices.
Boundaries and maturity
This document is current conceptual design, not formal grammar, a complete layout/ABI contract, or an implementation mapping.
Future work owns language- and provider-supplied partials, generic identity factories, and reflection APIs. Structural shapes and compatibility, current composition, and current enum behavior preserve the explicit identity boundary and admission/exposure choices defined here.