Net
TCP sockets, over native_stdlib/net_shim.c. Plain Result wrapping around a sentinel -1: a raw BSD sockets call crossing the shim does not preserve an errno, so each failure carries a fixed message saying WHAT failed rather than why.
let connect_to (host: String) (port: Int): Result[Int, String]
Opens a TCP connection. The Int is a socket, for Net.read, Net.write and Net.close.
host may be a name or an address; resolution happens here, so a DNS failure and a refused connection are both Err with a message saying which.
Blocking, and never closed for you. There is no timeout, and no handle type — a socket outlives every scope until Net.close is called, so a connection opened in a loop and dropped is a leak the language will not catch.
let listen_on (port: Int): Result[Int, String]
Binds and listens on a port, returning a socket to accept on.
Binds all interfaces. Err when the port is already in use or is one this process may not have — under 1024 usually needs privilege.
let accept (fd: Int): Result[Int, String]
Waits for a connection and returns a socket for it.
Blocks until a client arrives. The listening socket stays open for the next accept; the returned one is a separate connection and needs its own Net.close.
let write (fd: Int) (data: String): Result[Int, String]
len MUST be bound BEFORE .as_cstr() runs. .as_cstr() decrements the string's refcount to produce its copy and FREES the original outright if that was the last reference, exactly what a direct tcp_write(fd, "hello") passes. Reading data.len() afterwards in the same argument list is a real use-after-free, and the real compiler found it the hard way: garbage bytes trailing the content on an actual socket, observed on the wire rather than theorized.
let read (fd: Int) (max_len: Int): String
"" on BOTH a clean peer close and a hard socket error. The distinction is not preserved in v1, and either way "stop reading" is the right response, and an empty String already signals to a caller looping until end of stream.
let read_bytes (fd: Int) (max_len: Int): Result[Bytes, String]
Three outcomes, and the middle one is why this exists as a Result[Bytes, _] rather than just Bytes:
Ok(bytes) with a non-empty buffer: data Ok(empty) the peer closed cleanly: end of stream Err(..) a real socket error
Net.read collapses the last two into "", a documented v1 trade that a text protocol can live with because either answer means "stop reading". A length-framed binary protocol cannot: an empty read mid-stream is an ordinary event, and telling it from a dropped connection is the difference between waiting and failing. tcp_recv_n returns a count and keeps the two apart.
let write_bytes (fd: Int) (data: Bytes): Result[Int, String]
len MUST be bound BEFORE .as_cstr() runs, for the same reason write above spells that out: the pointer is an interior one into the cell, and reading .len() afterwards in the same argument list is how that bug was found on the wire the first time.
Binary-safe because tcp_send takes an explicit length and never calls strlen, so a NUL in the buffer is just another byte.
let close (fd: Int): Unit
Closes a socket. Closing one twice, or closing a number that was never a socket, does nothing rather than failing.