Nex is a small language, but the runtime still provides a practical set of built-in classes and services. This appendix gives a tutorial-oriented overview of what is available at the library level and how those pieces fit together.
The material here is drawn from the runtime behavior documented in the Nex Reference and the supporting guides under docs/md.
The most commonly used built-in service classes are:
ConsoleProcessTaskChannelFor filesystem and file I/O, use the lib/io library:
intern io/Pathintern io/Directoryintern io/Text_Fileintern io/Binary_FileTogether with the scalar and collection classes, they form the everyday standard environment of Nex programs.
Other important shipped libraries include:
data/Byte_Arraymath/Random, for random numbers that can be repeated from a seedtime/Date_Timetime/Durationtext/Regexnet/Http_Clientnet/Http_ServerConsole supports interactive text programs.
Every program and REPL session has one already, predeclared as console. Should you need your own:
let con: Console := create Console
Useful operations:
print(msg)print_line(msg)flush()read_line(prompt?)error(msg)read_integer()read_real()Example:
console.print_line("What is your name?")
let name: String := console.read_line()
console.print_line("Hello, " + name)
Filesystem operations live in the io library.
Path is the main entry point for filesystem probing and convenience file operations.
Construction:
intern io/Path
let p: Path := create Path.make("notes.txt")
Useful operations:
exists()is_file()is_directory()size()modified_time()read_text()write_text(text)append_text(text)copy_to(target)move_to(target)delete()delete_tree()Example:
intern io/Path
let src: Path := create Path.make("notes.txt")
src.write_text("line 1")
src.append_text("\nline 2")
let copy: Path := create Path.make("notes_copy.txt")
src.copy_to(copy)
print(copy.read_text())
let moved: Path := create Path.make("notes_moved.txt")
copy.move_to(moved)
print(moved.exists())
print(moved.size())
print(moved.modified_time())
For sequential text and binary access, use Text_File and Binary_File. A Binary_File reads and writes bytes as an Array[Byte], so writer.write("ABC".to_bytes()) writes three bytes and reader.read_all() returns them as Byte values.
If your code is directory-oriented, use Directory as a thin wrapper over Path.
intern io/Path
intern io/Directory
let root_path: Path := create Path.make("tmp")
if root_path.exists() then
root_path.delete_tree()
end
let root: Directory := create Directory.make("tmp")
root.create_tree()
let data: Directory := root.child_dir("data")
data.create_tree()
let file: Path := data.child_path("items.txt")
file.write_text("one\ntwo")
let backup: Directory := root.child_dir("backup")
data.copy_to(backup)
print(backup.exists())
print(root.directories().length)
print(data.files().length)
intern io/Path
intern io/Text_File
let path: Path := create Path.make("notes.txt")
let writer: Text_File := create Text_File.open_write(path)
writer.write_line("alpha")
writer.write_line("beta")
writer.close()
Use file routines at the boundary of the system. Core logic should usually operate on strings, arrays, maps, and classes rather than on filesystem objects directly.
The predeclared program (of type Program) describes the running program.
Useful operations:
arguments — the words after the script name, as an Array[String]env(name) — an environment variable, or "" when it is not sethas_env(name)working_directory — the folder the program was started from; build relative file paths from itpidexit(code)Example:
print(program.arguments)
print(program.env("HOME"))
print(program.working_directory + "/notes.txt")
The older Process class (create Process) also offers getenv(name), like program.env, and command_line(), the same list as program.arguments.
Use spawn to start concurrent work and Channel[T] to move values between tasks.
let jobs: Channel[String] := create Channel[String].with_capacity(2)
let worker: Task := spawn do
let item := jobs.receive
print("worker saw " + item)
end
jobs.send("compile docs")
worker.await
Channels can be buffered or unbuffered. Unbuffered channels synchronize sender and receiver directly; buffered channels allow a limited number of queued values. select lets one task wait on several channel operations at once.
Use time/Date_Time and time/Duration for UTC timestamps, scheduling offsets, and log formatting.
intern time/Duration
intern time/Date_Time
let started_at: Date_Time := create Date_Time.now()
let next_run: Date_Time := started_at.add(create Duration.minutes(15))
let weekly_cutoff: Date_Time := started_at.add(create Duration.weeks(1))
print("started at " + started_at.format_iso())
print("month=" + started_at.month_name())
print("weekday=" + started_at.weekday())
print("weekday-name=" + started_at.weekday_name())
print("day-of-year=" + started_at.day_of_year())
print("next run at " + next_run.truncate_to_hour().format_iso())
print("weekly cutoff " + weekly_cutoff.truncate_to_day().format_iso())
This is a better fit for logging and scheduling code than manually building timestamp strings.
Use text/Regex when string operations alone are too weak for validation, token extraction, or cleanup.
intern text/Regex
let word: Regex := create Regex.compile_with_flags("[a-z]+", "i")
print(word.matches("Nex"))
print(word.find("123 Nex 456"))
print(word.find_all("one two THREE"))
let comma: Regex := create Regex.compile(",")
print(comma.split("a,b,c"))
print(word.replace("v1 test v2", "#"))
This is useful for:
Keep regex usage near parsing and validation boundaries. Higher-level domain logic should usually work on already structured values.
The net library provides client and server building blocks when a Nex program needs to talk over HTTP.
intern net/Http_Client
let client: Http_Client := create Http_Client.make()
let sample: Http_Response := create Http_Response.make(
200,
"ok",
{"content-type": "text/plain"}
)
print(sample.status())
print(sample.body())
For server-side code, Http_Server and related request/response classes let a program register handlers and return structured responses. This is host-backed functionality, so exact behavior can differ between runtimes.
Much of the practical “standard library” feel of Nex comes from Array, Map, and Set.
Use arrays for:
Use maps for:
Use sets for:
These classes are generic and work with user-defined classes just as naturally as with built-in scalar values.
Byte_Array: Packed BytesAn Array[Byte] is a list of boxed values, several times larger than the data it holds. For large amounts of binary data, data/Byte_Array is a fixed-size buffer stored as a real Java byte[], one byte per element. Load it with intern data/Byte_Array:
intern data/Byte_Array
let buf: Byte_Array := create Byte_Array.make(4)
buf.set(0, 200u8)
buf.set(3, 65u8)
print(buf.get(0)) -- 200
print(buf) -- Byte_Array([200, 0, 0, 65])
print(buf.slice(2, 4)) -- Byte_Array([0, 65])
print(buf.to_hex()) -- "c8000041"
across buf as b do
print(b) -- each b is a Byte
end
The main operations are:
make, from_array, from_java to create one (the last two copy their input)length, get, set, fill, index_of, containsslice, copy, concat, copy_into — these copy, so two Byte_Arrays never share storage by accidentto_array, to_hex, to_utf8_stringequals and compare work on the contents, so =, < and sort behave as you would expectto_java, which hands the underlying byte[] to Java code inside a with "java" blockThe size is fixed: there is no add. Use an Array[Byte] when the length changes. The elements are always Bytes (0 to 255), even though Java stores them as signed bytes; Nex converts at the boundary.
The to_java method removes the usual detour when calling Java APIs that want a byte[]:
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]
acrossThe across loop depends on cursor types behind the scenes:
ArrayCursorStringCursorMapCursorSetCursorByte_Array_Cursor, returned by Byte_Array.cursor(); its items are BytesYou will usually not construct these directly. Each is a Cursor[T], and every collection that hands one out is an Iterable[T]; that is what makes one iteration form work uniformly across strings, arrays, sets, maps, and classes of your own (Section 15.8).
Use the runtime library in layers.
At the core:
At the edge:
This separation keeps the program testable and helps contracts remain meaningful.
Two small sum-type libraries handle the recurring shapes of “success or failure” and “present or absent.” Both are loaded with intern and both are documented in full in the language reference.
data/Result provides Result[T, E] — either Ok carrying a value of type T, or Err carrying an error of type E:
intern data/Result
let r: Result[Integer, String] := create Ok[Integer, String].make(42)
print(r.unwrap_or(0)) -- 42
match r of
Ok(value) then print(value)
Err(error) then print(error)
end
Query and unwrap are methods: is_ok, is_err, unwrap_or. Transformations are free functions, since each introduces a new type: result_map (map the success value), result_and_then (chain a fallible step, short-circuiting on the first error), and result_map_err (convert the error type).
data/Option provides Option[T] — either Some carrying a value, or None. It is a typed, exhaustively-checked alternative to a detachable ?T, with methods is_some, is_none, get_or, and the combinators option_map, option_and_then, option_filter.
intern data/Option
let o: Option[Integer] := create Some[Integer].make(10)
print(o.get_or(0)) -- 10
Both run on the JVM interpreter and compiled backends. (The combinator functions do not yet run under the generated-JavaScript backend.)
Nex does not try to ship a huge standard library inside the tutorial material. The core design assumes that:
intern and importThat is what Chapter 24 is for. The standard library is enough to be productive, but it is not meant to be the whole world.
For the complete, up-to-date method reference — covering built-in functions, scalar types, collection types, cursor types, system classes, and all shipped libraries — see the Nex Reference.