cairnlang-core 0.5.0

Cairn core: content-addressed AST store, the single type/confidence/effect checker, projection renderer, and WASM lowering. Owns the model.
Documentation
//! The value-level model: types, confidence, effects.
//!
//! These are the fixed, closed vocabularies from `docs/design.md` Section 4.
//! They are deliberately small; nothing is added here that a check does not
//! need.

use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;

/// The v1 type set. `Named` is a user-defined record or variant referenced
/// by name; its definition is supplied via `define_type`/`TypeDefSpec`
/// (shipped — records carry fields, variants carry cases, and the checker
/// resolves them: field access is typed, `Match` is exhaustiveness-checked).
/// `Named` is equal **by name** — nominal, monomorphic type identity. That
/// is the *decided* design (§9, Principle 9: one canonical form), not a
/// stopgap: there is no structural or generic type equality, by choice.
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum Type {
    Number,
    /// An IEEE-754 double. Travels in the uniform i64 slot as its bit
    /// pattern; only float operations reinterpret it.
    Float,
    /// Fixed-point decimal: an i64 scaled by 10_000 (four fractional
    /// digits). Exact, no NaN — the money/review-safe non-integer type.
    Decimal,
    String,
    Bool,
    Bytes,
    List(Box<Type>),
    Map(Box<Type>, Box<Type>),
    Option(Box<Type>),
    Result(Box<Type>, Box<Type>),
    Named(String),
    /// A mutable cell holding a `T` (the `Mut` effect's value type).
    Cell(Box<Type>),
    /// A first-class function value. `effects` are what *calling* it
    /// performs; they union into the caller at the call site. The bottom
    /// confidence of a call's value is conservative (Structural), so the
    /// produced confidence is not part of the type (a documented v0.4
    /// simplification — it only ever under-claims, never over-claims).
    Fn {
        params: Vec<Type>,
        ret: Box<Type>,
        effects: BTreeSet<Effect>,
    },
    /// A type variable: a function's declared type parameter (e.g. `T`).
    /// Within a generic function's body it is an opaque nominal type; at a
    /// call site it is unified against the actual argument types.
    Var(String),
    /// The bottom type: the "type" of an expression that diverges (`fail`)
    /// and never yields a value. Compatible with every type, because an
    /// expression that never returns can stand wherever a value is expected.
    Never,
}

/// Epistemic status. The derived `Ord` is the total order from Section 4 —
/// declaration order is the order: `External < Structural < Validated <
/// Persisted`. Weakest-input propagation is `min` over this order.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum Confidence {
    External,
    Structural,
    Validated,
    Persisted,
}

/// The nine effect primitives. `Ord` is derived so effect sets can be a
/// `BTreeSet`, which keeps node serialization (and therefore hashing)
/// deterministic.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum Effect {
    Net,
    Disk,
    Db,
    Mut,
    Time,
    Rand,
    Log,
    /// Out-of-band UI push (design.md §10): `publish(topic)` performs it.
    /// Making liveness an effect keeps it visible in signatures and
    /// checked — never a hidden default.
    Live,
    /// Response shaping: `set_header(name, value)` emits an extra HTTP
    /// response header (e.g. `Set-Cookie`). Buffered per request on the
    /// same thread-local seam as `publish`/`Live`, drained and emitted
    /// by the host server. An effect, not a `Response` field, so it is
    /// visible in signatures and checked — never a hidden default
    /// (design.md §9).
    Resp,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn confidence_total_order_matches_section_4() {
        use Confidence::*;
        assert!(External < Structural);
        assert!(Structural < Validated);
        assert!(Validated < Persisted);
        // weakest-input propagation is `min`
        assert_eq!(std::cmp::min(Persisted, External), External);
        assert_eq!(std::cmp::min(Validated, Structural), Structural);
    }
}