module shape

module lib/shape.mu

import "shape"

shape provides helpers for tagged map values.

A shape is still an ordinary map. The __type field names a recurring data contract so type(value) reports that name instead of "MAP".

Imports

Functions

is#

fn is(value, type_name)

is reports whether value carries the provided runtime type tag.

Source lib/shape.mu:113
fn is(value, type_name) {
    if !typing.is_str(type_name) {
        return false
    }
    return type(value) == type_name
}

kind#

fn kind(value)

kind returns value.kind for map-like values, or nil when no kind is present.

Source lib/shape.mu:154
fn kind(value) {
    if !_is_map_like(value) || !_has_key(value, "kind") {
        return nil
    }
    return value["kind"]
}

new#

fn new(type_name, fields, methods...)

new returns a tagged map by copying fields and optional methods into a fresh value. Method entries must be functions and may not replace fields.

Source lib/shape.mu:84
fn new(type_name, fields, methods...) {
    if !typing.is_str(type_name) || type_name == "" {
        return error("shape.new expects a non-empty string type name")
    }
    if !_is_map_like(fields) {
        return error("shape.new expects fields map, got " + type(fields))
    }
    if len(methods) > 1 {
        return error("shape.new expects at most one methods map")
    }
    value := {}
    err := _copy_fields(fields, value, "shape.new fields")
    if is_error(err) {
        return err
    }
    if len(methods) == 1 && methods[0] != nil {
        if !_is_map_like(methods[0]) {
            return error("shape.new expects methods map, got " + type(methods[0]))
        }
        err = _copy_methods(methods[0], value)
        if is_error(err) {
            return err
        }
    }
    value["__type"] = type_name
    return value
}

require#

fn require(value, type_name)

require returns nil when value carries type_name, otherwise an error value.

Source lib/shape.mu:121
fn require(value, type_name) {
    if !typing.is_str(type_name) || type_name == "" {
        return error("shape.require expects a non-empty string type name")
    }
    if type(value) != type_name {
        return error("shape.require expected " + type_name + ", got " + type(value))
    }
    return nil
}

require_fields#

fn require_fields(value, fields)

require_fields verifies that value is a map-like value with all named fields.

Source lib/shape.mu:132
fn require_fields(value, fields) {
    if !_is_map_like(value) {
        return error("shape.require_fields expects map-like value, got " + type(value))
    }
    if !typing.is_list(fields) {
        return error("shape.require_fields expects list of field names, got " + type(fields))
    }
    i := 0
    while i < len(fields) {
        field := fields[i]
        if !typing.is_str(field) {
            return error("shape.require_fields expects string field names, got " + type(field))
        }
        if !_has_key(value, field) {
            return error("shape.require_fields missing " + field)
        }
        i = i + 1
    }
    return nil
}

require_kind#

fn require_kind(value, expected)

require_kind verifies the conventional kind discriminator on a map-like value.

Source lib/shape.mu:162
fn require_kind(value, expected) {
    if !typing.is_str(expected) || expected == "" {
        return error("shape.require_kind expects a non-empty string kind")
    }
    if kind(value) != expected {
        return error("shape.require_kind expected " + expected)
    }
    return nil
}

Internal helpers

Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.

_copy_fields(source, target, context)
_copy_methods(source, target)
_has_key(value, key)
_is_map_like(value)
_keys(value)