Documentation
File I/O
File I/O
File is a builtin reference-managed class for a deliberately small native,
POSIX text and binary file interface. open(...) is a compiler-backed factory
adapter to File(...): both create the same class object through one checked
construction path. Both are available without an import and use C stdio, not
Python or Arrow. Lowercase file remains a compatibility spelling for the
builtin File type.
The default is a strict UTF-8 text read. Use mode="w" to create/truncate or
mode="a" to append. Only the path and mode can be runtime strings; other
Python-shaped settings accept only the implemented default profile. Nondefault
buffering, encodings, error policies, newline handling, descriptors, and openers
are rejected rather than ignored. Options are checked before I/O; a dynamic mode
is checked before opening or truncating a path.
Paths are relative to the running program's working directory, not its source file.
Reading requires an existing file. Writing creates or truncates it. Appending
creates it if necessary and writes at the end. Update/exclusive modes, file
descriptors, callbacks, and custom buffering are unsupported. Binary modes are
rb, wb, and ab; non-none encoding, errors, or newline options are
rejected before creating/truncating a path, including when the mode is dynamic.
Example
Run this in a scratch directory: the example creates/truncates message.txt.
```
title: Native text file example
```
fn main() -> i32:
```
title: main
```
var output: File = File("message.txt", mode="w", encoding="utf-8")
var alias: file = output
assert alias.write("hello\n") == 6
output.close()
alias.close()
var source: file = open(file="message.txt")
var text: str = source.read()
source.close()
print(text)
return 0
Bounded binary workflow
Use bytes([0, 255, 13, 10]) for octets, then output.write_bytes(data) on a
binary stream. source.read_bytes(size) reads at most a nonnegative i64 byte
count; there is no read-all sentinel. It returns independently owned bytes,
including empty at EOF. Zero does not advance but still checks mode and closed
state. Text methods reject binary streams and byte methods reject text streams.
Nothing decodes, strips NUL, or translates newlines.
examples/binary-files.x is an executable round-trip and append example. It
creates/truncates binary-output.dat and binary-empty.dat; run it in a
scratch directory. Byte lengths and write counts are bytes, not characters.
Short writes that make progress are retried; no progress and I/O errors are
failures. Failure may leave a partially modified external file: there is no
rollback, atomicity, or durability promise. Explicit flush/close still matter
for buffered errors.
See byte values for immutable-buffer ownership.
Text and lifetime safety
Reads consume all remaining text (not a size-limited chunk), returning an
independent string or "" at EOF. CRLF and CR become LF; malformed UTF-8 and
embedded NUL fail. Write counts are Unicode characters, not bytes. Explicit
flush checks buffered writes without promising durability. Close is idempotent;
other operations on a closed file fail. Method signatures and concise contracts
live in packages/arx/src/arx/builtins/files.x.
Public File values follow ordinary acyclic class-reference ownership. Aliases
share one stream and cursor; closing through one reference closes it for all
references. References can be reassigned, passed to functions, returned, and
stored in class fields. Locals and discarded temporaries release their
references on ordinary lexical exits. The last reference releases the native
wrapper once, even if the stream was explicitly closed earlier. Read strings and
byte buffers own independent memory and can outlive the file. See
Classes.
Explicit casts to or from builtin File, including identity casts, remain
unsupported. Use ordinary assignment to create an alias instead.
Construction opens immediately: there is no public unopened constructor,
instance open() method, reopening, or builtin subclassing. A default-empty
File reference is not an unopened stream; dereferencing it raises the checked
class-reference error. Class containers/unions/struct fields, owned globals and
statics, generator capture, and external object ownership remain unsupported.
The class's native handle is private and uniquely owned. It cannot be extracted,
copied, returned, or replaced by user code; NativeFileHandle is not a public
Arx type. General class finalization owns its release, not a second manual
handle-freeing destructor.
Direct creation and I/O failures use ARX-RUNTIME-FILE-001 and raise typed
exceptions (including OSError for operating-system failures). Class checks use
ARX-RUNTIME-CLASS-001; allocation failure remains unrecoverable. Failed
creation preserves constructor rollback and completed-base cleanup. See the
exception boundary.
Explicit close/flush are recommended to observe write errors before later work.
with, iteration, seek/tell, and extra file properties are deferred.
For explicit imports, use import File as TextFile from builtins.files or
import builtins.files as io followed by io.File(...). These resolve the same
canonical class, not an unrelated class with the same name. Existing user or
imported File/open bindings retain normal resolution; non-callable local
shadowing is an error. The lowercase file type always denotes the builtin.
This is a Python-like subset: parameter names/order/default spellings follow
Python's interface,
but UTF-8 replaces locale-dependent encoding, and NUL-containing text and many
optional settings are intentionally unsupported. Existing user/imported
functions named open retain normal positional-call resolution. A local
non-callable open shadows the builtin and produces an error when called.