Skip to content

std/dynamic

Import with import std/dynamic.

type Dynamic

Opaque wrapper for a value of unknown shape returned by an extern function or decoded from an external source such as JSON, YAML, RPC payloads, or database rows.

Dynamic values are produced by externs that declare Dynamic in their return type. Pure Nomi code cannot construct one directly. To pass a typed Nomi value to host code as a dynamic value, call an extern that accepts a Dynamic argument and let the runtime marshal it for the host.

Inspect via Dynamic.field/index/path navigators and Dynamic.as_string/as_int/… extractors. Each step returns Result<_, DecodeError> with a structured path; chain with try to propagate.

fn field(d: Dynamic, name: String): Result<Dynamic, DecodeError>

type function on Dynamic

Drill into a struct/dict-shaped Dynamic by field name. Fails with expected="object with field <name>" if d is a dict but doesn’t have the field, or expected="object" if d is not a dict at all.

fn index(d: Dynamic, i: Int): Result<Dynamic, DecodeError>

type function on Dynamic

Drill into a list-shaped Dynamic by zero-based index. Fails with expected="list with at least <i+1> elements" if d is a list but the index is out of bounds, or expected="list" if d is not a list at all.

fn path(d: Dynamic, dotted: String): Result<Dynamic, DecodeError>

type function on Dynamic

Convenience: dotted-path navigator. Dynamic.path(d, "a.b.c") is equivalent to d |> Dynamic.field("a") |> try |> Dynamic.field("b") |> try |> Dynamic.field("c"). Does NOT support index notation (use index directly for lists). Empty segments (e.g. "a..b") error.

fn as_string(d: Dynamic): Result<String, DecodeError>

type function on Dynamic

Extract a Dynamic as a String. Err on type mismatch.

fn as_int(d: Dynamic): Result<Int, DecodeError>

type function on Dynamic

Extract a Dynamic as an Int. Accepts integer-shaped Floats (e.g. JSON’s number 36.0) but rejects values with a fractional part.

fn as_float(d: Dynamic): Result<Float, DecodeError>

type function on Dynamic

Extract a Dynamic as a Float. Accepts Ints (widening) and Floats.

fn as_bool(d: Dynamic): Result<Bool, DecodeError>

type function on Dynamic

Extract a Dynamic as a Bool. Err on type mismatch.

fn as_list(d: Dynamic): Result<List<Dynamic>, DecodeError>

type function on Dynamic

Extract a Dynamic as List<Dynamic> — each element is still wrapped, so per-element decoding (List.map_result(decode_one)) composes through as_list. Err if d is not a list.

fn as_dict(d: Dynamic): Result<Map<String, Dynamic>, DecodeError>

type function on Dynamic

Extract a Dynamic as Map<String, Dynamic> — each value is still wrapped, so per-value decoding (map iteration + as_X) composes. Err if d is not a dict/object.

fn null?(d: Dynamic): Bool

type function on Dynamic

True iff d represents a null/nil value at the source (JSON null, Go nil, etc.). Never errors.

fn has?(d: Dynamic, name: String): Bool

type function on Dynamic

True iff d is a dict and contains the named field. False otherwise (including when d is not a dict). Never errors.

fn as_maybe<'T>(d: Dynamic, sub: (Dynamic) -> Result<'T, DecodeError>): Result<Maybe<'T>, DecodeError>

type function on Dynamic

Apply sub to d only if d is non-null. Null → Ok(None); present → Ok(Some(try sub(d))).

The ergonomic helper for JSON’s ubiquitous nullable fields.

d
|> Dynamic.as_maybe(Dynamic.as_string)
|> try
// → Maybe<String> from the value (or None if null).
fn at_field<'T>(d: Dynamic, name: String, sub: (Dynamic) -> Result<'T, DecodeError>): Result<'T, DecodeError>

type function on Dynamic

Navigate into a struct/dict field AND apply a sub-decoder, accumulating the path segment on any error the sub-decoder returns. The chain

d |> Dynamic.at_field("user", |inner| inner
  |> Dynamic.at_field("address", |inner2| inner2
    |> Dynamic.at_field("zip", Dynamic.as_int)))

produces a DecodeError with path = [Field("user"), Field("address"), Field("zip")] if as_int fails, vs. the raw form

d
  |> Dynamic.field("user")
  |> try
  |> Dynamic.field("address")
  |> try
  |> Dynamic.field("zip")
  |> try
  |> Dynamic.as_int()

which produces an empty-path error because try returns Err unchanged. Use this when you want path-rich diagnostics; the raw form stays available when you don’t.

Field-missing failures from the navigator itself pass through unchanged — they already carry [Field(name)] from field’s own error, so the path is not doubled.

fn at_index<'T>(d: Dynamic, i: Int, sub: (Dynamic) -> Result<'T, DecodeError>): Result<'T, DecodeError>

type function on Dynamic

List-index counterpart to at_field. Same path-accumulation behaviour: prepends Index(i) to any error the sub-decoder returns; navigator failures (out-of-bounds, non-list) pass through unchanged.

impl Debug

fn inspect(d: Dynamic): String

impl Debug.inspect

enum PathSegment {
    Field String
    Index Int
}

One segment of a DecodeError’s path. Field for a dict-key navigation, Index for a list-index navigation.

impl Display

fn to_string(value: PathSegment): String

impl Display.to_string

impl Debug

fn inspect(value: PathSegment): String

impl Debug.inspect

struct DecodeError {
    path: List<PathSegment>
    expected: String
    got: String
}

Structured decode failure. path locates the failure site; one segment per navigator that contributed to this error. Raw try chains short-circuit on Err unchanged, so a chain like d |> Dynamic.field("a") |> try |> Dynamic.field("b") |> try |> Dynamic.as_int() produces an empty-path error if as_int fails — the outer navigators never see the inner error to prepend their segment. Use at_field / at_index when you want path-rich diagnostics; those combinators wrap a navigator + sub-extractor and prepend the path segment on any error the sub-extractor returns. expected and got name the type mismatch.

impl Display

fn to_string(e: DecodeError): String

impl Display.to_string

impl Debug

fn inspect(value: DecodeError): String

impl Debug.inspect