Skip to content

Identifiers

Stable identifiers and source provenance for the kernel.

identifiers

Stable identifiers and source provenance for the QIEC kernel.

Identifiers in the kernel are content-derived references, not process-local object identities. This keeps the serialized core independent of Python objects and gives migrations a stable target when source locations move.

QIEC_ABI module-attribute

QIEC_ABI = 'qiec-core/v1alpha1'

The kernel ABI tag a module records, so a reader can reject a module built against another kernel.

AddressRelation

AddressRelation = Literal['preserve', 'split', 'duplicate', 'eliminate']

How an effect request relates to its source site.

"preserve" marks the request as written; "split" and "duplicate" mark a request a handler derived from it; "eliminate" marks one a handler discharged.

StableId dataclass

StableId(digest: str)

A namespaced, SHA-256-derived kernel identifier.

The human-readable declaration name is intentionally not stored in the identifier. Renaming display labels must not silently alter equality; callers choose the declaration path supplied to :meth:derive and record renames explicitly in a migration map.

PARAMETER DESCRIPTION
digest

The 64-character lowercase hex SHA-256 digest; usually produced by :meth:derive rather than written by hand.

TYPE: str

__post_init__

__post_init__() -> None

Reject a digest that is not 64 lowercase hex characters.

RAISES DESCRIPTION
ValueError

If the digest is the wrong length or contains a character outside 0-9a-f.

Source code in src/quivers/qiec/identifiers.py
def __post_init__(self) -> None:
    """Reject a digest that is not 64 lowercase hex characters.

    Raises
    ------
    ValueError
        If the digest is the wrong length or contains a character
        outside ``0-9a-f``.
    """
    if len(self.digest) != 64 or any(
        c not in "0123456789abcdef" for c in self.digest
    ):
        raise ValueError(f"invalid {self.namespace} digest: {self.digest!r}")

derive classmethod

derive(*parts: object) -> Self

Derive an identifier from the values that determine it.

PARAMETER DESCRIPTION
parts

The determining values, in order. Order matters, so two identifiers built from the same values differently arranged are distinct.

TYPE: object DEFAULT: ()

RETURNS DESCRIPTION
Self

The derived identifier. The ABI version and the namespace are mixed in, so an identifier from one namespace can never equal one from another, and a future ABI change re-derives rather than silently reusing.

RAISES DESCRIPTION
TypeError

If a part has no canonical encoding.

Source code in src/quivers/qiec/identifiers.py
@classmethod
def derive(cls, *parts: object) -> Self:
    """Derive an identifier from the values that determine it.

    Parameters
    ----------
    parts : object
        The determining values, in order. Order matters, so two
        identifiers built from the same values differently arranged
        are distinct.

    Returns
    -------
    Self
        The derived identifier. The ABI version and the namespace are
        mixed in, so an identifier from one namespace can never equal
        one from another, and a future ABI change re-derives rather
        than silently reusing.

    Raises
    ------
    TypeError
        If a part has no canonical encoding.
    """
    payload = (QIEC_ABI, cls.namespace, *parts)
    return cls(sha256(_canonical_bytes(payload)).hexdigest())

parse classmethod

parse(text: str) -> Self

Read an identifier back from its wire form.

PARAMETER DESCRIPTION
text

The wire form, qiec:<namespace>:<digest>.

TYPE: str

RETURNS DESCRIPTION
Self

The parsed identifier.

RAISES DESCRIPTION
ValueError

If the text carries another namespace's prefix, or the remainder is not a valid digest. Parsing is namespace-checked so a handler identifier cannot be read as an effect one.

Source code in src/quivers/qiec/identifiers.py
@classmethod
def parse(cls, text: str) -> Self:
    """Read an identifier back from its wire form.

    Parameters
    ----------
    text : str
        The wire form, ``qiec:<namespace>:<digest>``.

    Returns
    -------
    Self
        The parsed identifier.

    Raises
    ------
    ValueError
        If the text carries another namespace's prefix, or the
        remainder is not a valid digest. Parsing is namespace-checked
        so a handler identifier cannot be read as an effect one.
    """
    prefix = f"qiec:{cls.namespace}:"
    if not text.startswith(prefix):
        raise ValueError(f"expected {prefix!r} identifier, got {text!r}")
    return cls(text.removeprefix(prefix))

__str__

__str__() -> str

The wire form of this identifier.

RETURNS DESCRIPTION
str

qiec:<namespace>:<digest>, which parse accepts.

Source code in src/quivers/qiec/identifiers.py
def __str__(self) -> str:
    """The wire form of this identifier.

    Returns
    -------
    str
        ``qiec:<namespace>:<digest>``, which `parse` accepts.
    """
    return f"qiec:{self.namespace}:{self.digest}"

to_data

to_data() -> str

Return the stable wire representation.

RETURNS DESCRIPTION
str

The same string as __str__, named for the serialization protocol the rest of the kernel uses.

Source code in src/quivers/qiec/identifiers.py
def to_data(self) -> str:
    """Return the stable wire representation.

    Returns
    -------
    str
        The same string as `__str__`, named for the serialization
        protocol the rest of the kernel uses.
    """
    return str(self)

TypeId dataclass

TypeId(digest: str)

Bases: StableId

Stable identity of a type.

FamilyId dataclass

FamilyId(digest: str)

Bases: StableId

Stable identity of an indexed type family.

ConstructorId dataclass

ConstructorId(digest: str)

Bases: StableId

Stable identity of a family constructor.

EffectId dataclass

EffectId(digest: str)

Bases: StableId

Stable identity of an effect interface.

OperationId dataclass

OperationId(digest: str)

Bases: StableId

Stable identity of an effect operation.

EffectInstanceId dataclass

EffectInstanceId(digest: str)

Bases: StableId

Stable identity of an effect instance.

ComputationId dataclass

ComputationId(digest: str)

Bases: StableId

Stable identity of a named computation.

HandlerId dataclass

HandlerId(digest: str)

Bases: StableId

Stable identity of a handler.

AttachmentId dataclass

AttachmentId(digest: str)

Bases: StableId

Stable identity of a runtime attachment.

SiteId dataclass

SiteId(digest: str)

Bases: StableId

Stable identity of a source site.

EqualityId dataclass

EqualityId(digest: str)

Bases: StableId

Stable identity of an equality proposition.

StaticScopeId dataclass

StaticScopeId(digest: str)

Bases: StableId

Stable identity of a static scope.

StaticVariableId dataclass

StaticVariableId(digest: str)

Bases: StableId

Stable identity of a static variable.

RowVariableId dataclass

RowVariableId(digest: str)

Bases: StableId

Stable identity of a row variable.

PrimitiveId dataclass

PrimitiveId(digest: str)

Bases: StableId

Stable identity of a pure primitive.

DistributionId dataclass

DistributionId(digest: str)

Bases: StableId

Stable identity of a distribution family.

SourceOrigin dataclass

SourceOrigin(module: str, structural_path: tuple[str | int, ...], role: str, source_protocol: str, file: str | None = None, line: int | None = None, column: int | None = None)

Stable structural source origin with optional diagnostic coordinates.

structural_path is the protocol path after parsing and name resolution. Line and column are diagnostic metadata and therefore do not participate in :meth:site_id.

PARAMETER DESCRIPTION
module

The module the site belongs to.

TYPE: str

structural_path

The path of names and positions from the module root to the site.

TYPE: tuple[str | int, ...]

role

What the site is, such as "sample" or "call".

TYPE: str

source_protocol

The source language or protocol the path is expressed in.

TYPE: str

file

The file the site was read from, if any.

TYPE: str | None DEFAULT: None

line

The one-based line of the site, if known.

TYPE: int | None DEFAULT: None

column

The one-based column of the site, if known.

TYPE: int | None DEFAULT: None

site_id

site_id() -> SiteId

The stable identity of the source site this origin describes.

RETURNS DESCRIPTION
SiteId

An identity derived from the protocol, module, structural path, and role. The file, line, and column are deliberately excluded, so reformatting a source file does not change the identity of the sites within it.

Source code in src/quivers/qiec/identifiers.py
def site_id(self) -> SiteId:
    """The stable identity of the source site this origin describes.

    Returns
    -------
    SiteId
        An identity derived from the protocol, module, structural
        path, and role. The file, line, and column are deliberately
        excluded, so reformatting a source file does not change the
        identity of the sites within it.
    """
    return SiteId.derive(
        self.source_protocol,
        self.module,
        self.structural_path,
        self.role,
    )

to_data

to_data() -> dict[str, object]

Return the serializable form of this origin.

RETURNS DESCRIPTION
dict[str, object]

Every field, including the diagnostic coordinates. Those do not enter site_id but are carried here so a deserialized origin can still point at a line.

Source code in src/quivers/qiec/identifiers.py
def to_data(self) -> dict[str, object]:
    """Return the serializable form of this origin.

    Returns
    -------
    dict[str, object]
        Every field, including the diagnostic coordinates. Those do
        not enter `site_id` but are carried here so a deserialized
        origin can still point at a line.
    """
    return {
        "module": self.module,
        "structural_path": list(self.structural_path),
        "role": self.role,
        "source_protocol": self.source_protocol,
        "file": self.file,
        "line": self.line,
        "column": self.column,
    }

DynamicAddressFrame dataclass

DynamicAddressFrame(scope: str, key: str | int)

One serializable component of a dynamic effect address.

PARAMETER DESCRIPTION
scope

What kind of frame this is, such as a call, a handler clause, a resumption, or a local instance.

TYPE: str

key

The frame's position within its scope: a name or an ordinal.

TYPE: str | int

SiteProvenance dataclass

SiteProvenance(origin: SourceOrigin, dynamic_path: tuple[DynamicAddressFrame, ...] = (), resumption_path: tuple[int, ...] = (), relation: AddressRelation = 'preserve', parents: tuple[SiteId, ...] = ())

Static and dynamic provenance of an effect request.

PARAMETER DESCRIPTION
origin

The source site the request was written at.

TYPE: SourceOrigin

dynamic_path

The address frames entered to reach this occurrence of the site, outermost first.

TYPE: tuple[DynamicAddressFrame, ...] DEFAULT: ()

resumption_path

The ordinal of each resumption taken to reach this occurrence, so a site reached twice under a multi-shot handler yields two keys.

TYPE: tuple[int, ...] DEFAULT: ()

relation

How this request relates to the source site: "preserve" for the request as written, "split" or "duplicate" for a request a handler derived from it, and "eliminate" for one it discharged.

TYPE: AddressRelation DEFAULT: 'preserve'

parents

The static sites a derived request was generated from.

TYPE: tuple[SiteId, ...] DEFAULT: ()

static_site property

static_site: SiteId

The source site this request was written at.

RETURNS DESCRIPTION
SiteId

The static identity, shared by every dynamic occurrence of the request.

dynamic_key

dynamic_key() -> tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

A hashable key distinguishing occurrences of one static site.

RETURNS DESCRIPTION
tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

The static site, the dynamic address frames, and the resumption path. One site reached twice, under a loop or under a resumed continuation, yields two keys, which is what lets a trace name each occurrence separately.

Source code in src/quivers/qiec/identifiers.py
def dynamic_key(
    self,
) -> tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]:
    """A hashable key distinguishing occurrences of one static site.

    Returns
    -------
    tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]
        The static site, the dynamic address frames, and the
        resumption path. One site reached twice, under a loop or
        under a resumed continuation, yields two keys, which is what
        lets a trace name each occurrence separately.
    """
    return (
        str(self.static_site),
        tuple((frame.scope, frame.key) for frame in self.dynamic_path),
        self.resumption_path,
    )

to_data

to_data() -> dict[str, object]

Return the serializable form of this provenance.

RETURNS DESCRIPTION
dict[str, object]

The origin, the static site, and the dynamic address. The static site is written out alongside the origin it derives from so a reader need not re-derive it.

Source code in src/quivers/qiec/identifiers.py
def to_data(self) -> dict[str, object]:
    """Return the serializable form of this provenance.

    Returns
    -------
    dict[str, object]
        The origin, the static site, and the dynamic address. The
        static site is written out alongside the origin it derives
        from so a reader need not re-derive it.
    """
    return {
        "origin": self.origin.to_data(),
        "static_site": self.static_site.to_data(),
        "dynamic_path": [
            {"scope": frame.scope, "key": frame.key} for frame in self.dynamic_path
        ],
        "resumption_path": list(self.resumption_path),
        "relation": self.relation,
        "parents": [parent.to_data() for parent in self.parents],
    }