Data Libraries

data/Json

Json is a small JSON parser and serializer shipped as a Nex library under lib/data/json.nex. Its methods are implemented on top of runtime json_parse and json_stringify primitives.

Loading

intern data/Json

Support

Target Supported
JVM REPL / interpreter Yes
Generated JVM code Yes

Construction

let json: Json := create Json.make()

Methods

Method Arguments Returns Description
make none Json Create a JSON helper object.
parse text: String Any Parse JSON text into Nex values.
stringify value: Any String Serialize Nex values into JSON text.

Value Mapping

  • JSON object -> Map[String, Any]
  • JSON array -> Array[Any]
  • JSON string -> String
  • JSON integer -> Integer
  • JSON decimal/exponent number -> Real
  • JSON boolean -> Boolean
  • JSON null -> nil

Example

intern data/Json

let json: Json := create Json.make()
let root: Map[String, Any] := json.parse("{\"name\":\"nex\",\"count\":3,\"items\":[1,2]}")
print(root.get("name"))
print(json.stringify(root))

Notes

  • parse returns Any, so callers usually bind the result to Map[String, Any] or Array[Any] when they know the expected shape.
  • stringify supports Nex Map, Array, scalar values, and nil.
  • Sets are serialized as JSON arrays.

data/Result

Result is a sealed sum type for computations that either succeed or fail, shipped as a Nex library under lib/data/result.nex. A Result[T, E] is either an Ok carrying a success value of type T, or an Err carrying an error of type E. The error type is independent of the value type, so an error threads up through calls without being rewrapped.

Loading

intern data/Result

intern data/Result brings the Result type, its Ok and Err variants, and the result_* combinator functions into scope.

Types

  • Result[T, E] — sealed deferred parent.
  • Ok[T, E] — success; field value: T.
  • Err[T, E] — failure; field error: E.

Support

Target Supported
JVM REPL / interpreter Yes
Generated JVM code Yes
Generated JavaScript No (free-function module wiring pending)

Construction

let ok:  Result[Integer, String] := create Ok[Integer, String].make(5)
let err: Result[Integer, String] := create Err[Integer, String].make("bad input")

Construction infers type arguments from the value: create Ok.make(5) is Ok[Integer, Any] and create Err.make("bad") is Err[Any, String]; the unmentioned parameter stays Any, so both assign to a Result[Integer, String]. Write explicit arguments to pin them.

Methods

Query and unwrap are methods (type-preserving).

Method Arguments Returns Description
is_ok none Boolean True when the result is an Ok.
is_err none Boolean True when the result is an Err.
unwrap_or fallback: T T The Ok value, or fallback when this is an Err.

Combinators

The transforming combinators are free functions, because each introduces a fresh type parameter.

Function Signature Description
result_map (r: Result[T, E], f: Function(x: T): U): Result[U, E] Apply f to the success value, leaving an Err untouched.
result_and_then (r: Result[T, E], f: Function(x: T): Result[U, E]): Result[U, E] Chain a fallible step, short-circuiting on the first Err (the bind / and_then).
result_map_err (r: Result[T, E], f: Function(x: E): F): Result[T, F] Transform the error channel, leaving an Ok untouched (converts Err[E] into a caller's error type).

Example

intern data/Result

function parse_positive(raw: Integer): Result[Integer, String] do
  if raw > 0 then
    result := create Ok[Integer, String].make(raw)
  else
    result := create Err[Integer, String].make("not positive")
  end
end

let doubled: Result[Integer, String] :=
  result_map(parse_positive(21), fn (x: Integer): Integer do result := x * 2 end)

match doubled of
  Ok(value)  then print(value)          -- 42
  Err(error) then print(error)
end

data/Option

Option is a sealed sum type for a value that may be present or absent, shipped under lib/data/option.nex. An Option[T] is either a Some carrying a value of type T, or None. It is a typed alternative to a detachable ?T: it survives through generic code and keeps the sealed-type exhaustiveness guarantee that plain nil does not.

Loading

intern data/Option

intern data/Option brings the Option type, its Some and None variants, and the option_* combinator functions into scope. Support matches data/Result: JVM interpreter and generated JVM code yes; generated JavaScript no.

Types

  • Option[T] — sealed deferred parent.
  • Some[T] — present; field value: T.
  • None[T] — absent; no fields.

Construction infers the type argument: create Some.make(42) is Some[Integer]; write create None[Integer].make() to pin None's parameter.

Methods

Method Arguments Returns Description
is_some none Boolean True when the option is a Some.
is_none none Boolean True when the option is None.
get_or fallback: T T The Some value, or fallback when this is None.

Combinators

Function Signature Description
option_map (o: Option[T], f: Function(x: T): U): Option[U] Apply f to the contained value, leaving None untouched.
option_and_then (o: Option[T], f: Function(x: T): Option[U]): Option[U] Chain an optional step, short-circuiting on None.
option_filter (o: Option[T], pred: Function(x: T): Boolean): Option[T] Keep a Some only when it satisfies pred; otherwise yield None.

Example

intern data/Option

let present: Option[Integer] := create Some[Integer].make(10)
let big: Option[Integer] :=
  option_filter(present, fn (x: Integer): Boolean do result := x > 5 end)

print(big.get_or(0))       -- 10

match big of
  Some(value) then print(value)
  None        then print("absent")
end

data/Sexpr

Sexpr is a minimal s-expression parser and serializer shipped as a pure-Nex library under lib/data/sexpr.nex. Unlike data/Json, it does not lean on any runtime parsing primitive — the parser is a hand-rolled character-cursor recursive descent over the input string, and the AST is an ordinary union type.

Loading

intern data/Sexpr

intern data/Sexpr brings the Sexpr type, its Symbol, Int, Float, Str, and List variants, the Sexpr_Parser class, and the parse_sexpr_text / sexpr_to_string functions into scope.

Types

  • Sexpr — union AST type.
  • Symbol(name: String) — a bare identifier, e.g. + or foo.
  • Int(value: Integer) — an integer literal.
  • Float(value: Real) — a decimal literal (requires a digit on both sides of the .).
  • Str(value: String) — a double-quoted string literal, with \, \", \n, \t, \r escapes.
  • List(items: Array[Sexpr]) — a parenthesized, whitespace-separated, recursively-nested sequence.

Support

Target Supported
JVM REPL / interpreter Yes
Generated JVM code Yes

Grammar

sexpr  := atom | list
list   := '(' sexpr* ')'
atom   := symbol | integer | float | string
symbol := any run of non-whitespace, non-paren, non-quote characters

Deliberately out of scope: comments, quote/quasiquote shorthand, dotted pairs, vectors.

Functions

Function Signature Description
parse_sexpr_text (text: String): Sexpr Parse text as a single s-expression. Trailing whitespace is allowed; any other trailing content raises.
sexpr_to_string (e: Sexpr): String Render a Sexpr back into s-expression text (round-trips parse_sexpr_text for any input using only the constructs above).

Malformed input (an unterminated list or string, a stray ), empty input) raises rather than returning a partial result.

Example

intern data/Sexpr

let e: Sexpr := parse_sexpr_text("(+ 1 (foo \"bar\" 2.5) -3)")
print(sexpr_to_string(e))

match e of
  List(items) then print(items.length)  -- 4
  else print("not a list")
end

Notes

  • Sexpr_Parser (constructed via create Sexpr_Parser.make(text), driven with .parse()) is the class parse_sexpr_text wraps; use it directly for incremental/streaming parsing.
  • Numeric tokens are classified by shape: a run of digits (optional leading +/-) is Int; the same with exactly one . and digits on both sides is Float; anything else is a Symbol — so operators like + and - parse as symbols, not numbers.

data/Byte_Array

Byte_Array is a fixed-size, mutable sequence of Bytes stored in a real Java byte[], one byte per element. Use it for binary data where Array[Byte] (a list of boxed values, several times larger) is too heavy, and to hand a real byte[] to Java code inside with "java". It is shipped as a Nex library under lib/data/byte_array.nex, on top of byte_array_* runtime primitives.

Loading

intern data/Byte_Array

Support

Target Supported
JVM REPL / interpreter Yes
Generated JVM code Yes

Construction

Constructor Arguments Description
make size: Integer size zero-filled bytes; raises if size is negative.
from_array items: Array[Byte] A copy of an Array[Byte].
from_slice source: Byte_Array, start: Integer, stop: Integer A copy of source[start, stop); what slice uses.
from_concat first: Byte_Array, second: Byte_Array A new array: first's bytes then second's; what concat uses.
from_java raw: Any A copy of a Java byte[]; raises for anything else.

Methods

Method Arguments Returns Description
length none Integer Number of elements.
get index: Integer Byte The element at index; raises if it is outside 0..length-1.
set index: Integer, value: Byte Void Store value at index; raises if it is out of range.
fill value: Byte Void Set every element to value.
copy none Byte_Array A copy of the whole array.
concat other: Byte_Array Byte_Array A new array: these bytes followed by other's.
copy_into target: Byte_Array, target_offset: Integer Void Copy all of these bytes into target starting at target_offset, overwriting what is there; raises unless they fit.
slice start: Integer, stop: Integer Byte_Array A copy of the elements in [start, stop); raises unless 0 <= start <= stop <= length.
index_of value: Byte Integer The first index holding value, or -1.
contains value: Byte Boolean True if value occurs.
to_array none Array[Byte] A boxed copy of the elements.
to_java none Any The underlying Java byte[], shared, not copied.
equals other: Any Boolean True for a Byte_Array with the same bytes. = uses this.
hash none Integer Hash of the contents.
compare other: Any Integer Lexicographic order with each byte taken as unsigned; a proper prefix orders first. Raises unless other is a Byte_Array.
to_hex none String Two lowercase hex digits per byte, e.g. "c3a9".
to_utf8_string none String Decode as UTF-8; raises if the bytes are not valid UTF-8.
cursor none Byte_Array_Cursor A cursor over the bytes; this is what across uses.
to_string none String Byte_Array([1, 2, 3]).

Notes

  • The storage holds Java's signed bytes, but the interface is unsigned: get returns a Byte in 0..255 and set keeps the low 8 bits of its Byte. So set(0, 200u8) then get(0) gives 200, while a Java caller sees -56.
  • The size is fixed; there is no add. Use Array[Byte] when the length changes.
  • Byte_Array is Comparable, so <, <=, >, >= work between two of them and an Array[Byte_Array] can be sorted.
  • across buf as b do ... end iterates the bytes, and b is a Byte (not Any). Byte_Array_Cursor is the cursor class (start, item, next, at_end) that cursor() returns; use it directly for a manual from c.start() until c.at_end() do ... end loop.
  • Everything copies except to_java, so two Byte_Arrays never alias each other by accident. Writes through the array to_java returns are visible in the Byte_Array.
intern data/Byte_Array
import java.security.MessageDigest

let input: Byte_Array := create Byte_Array.from_array("abc".to_bytes())
let digest: Any := nil
with "java" do
  let md := MessageDigest.getInstance("SHA-256")
  digest := md.digest(input.to_java())
end
let out: Byte_Array := create Byte_Array.from_java(digest)
print(out.length())                     -- 32
print(out.slice(0, 4).to_array())       -- [186, 120, 22, 191]

data/Mutex

Mutex[T] gives one task at a time exclusive access to a wrapped value. Unlike the Atomic_* classes (a single lock-free value), a Mutex protects a whole critical section — several statements against, typically, a mutable Array/Map/Set or object that must be read and written as one unit. It is shipped as a Nex library under lib/data/mutex.nex, built entirely on with "java" around java.util.concurrent.locks.ReentrantLock plus private feature fields — no runtime, typechecker, or compiler support of its own.

Loading

intern data/Mutex

Support

Target Supported
JVM REPL / interpreter Yes
Generated JVM code Yes

Construction

Constructor Arguments Description
make initial: T Wrap initial. The Mutex owns it from here — the only way back to it is use.

Methods

Method Arguments Returns Description
use body: Function(T): Void Void Acquire the lock, invoke body with the wrapped value, release when body returns or raises. Raises "Mutex.use: already held by this task" on self-reentrancy.

Notes

  • There is no lock/unlock/get — the wrapped value is reachable only inside a use callback, so it can neither be read without the lock nor leaked past the block. body mutates it in place (through its own methods); use itself always returns Void. If you need a result out, write it into an Atomic_Reference declared outside and set it inside body.
  • Release is guaranteed even when body raises (use's own rescue releases the lock and re-raises the same exception), so an exception mid-critical-section never leaves the Mutex held.
  • Not reentrant. A nested use on the same Mutex from the same task (an outer use's body calling use again on it) raises rather than silently succeeding (Java's synchronized) or silently deadlocking (Rust's std::sync::Mutex, a plain POSIX mutex).
  • Blocking or awaiting inside body — spawn, Task.await, a Channel send/receive, select, or use on a different Mutex — is not itself detected. Avoid it; a Mutex deadlocked this way looks like any other blocked acquire, not a raised error.
intern data/Mutex

let counters: Mutex[Map[String, Integer]] := create Mutex.make({})

counters.use(fn(m: Map[String, Integer]) do
  m.put("hits", m.try_get("hits", 0) + 1)
end)
counters.use(fn(m: Map[String, Integer]) do
  print(m.get("hits"))                  -- 1
end)