Package basic

Built-in package

Modules

Console

Terminal input and output.

let console = Console.Open
console.print("Hello from Par!")
console.close
type Console = iterative choice {
  .close => !,
  .print(String) => self,
  .prompt(String) => (Try<!, String>) self,
}

An interactive terminal handle.

  • .close — close the console.
  • .print(s) — print a string followed by a newline.
  • .prompt(s) — print a prompt string (no newline), read a line from stdin. Returns .ok with the input (trailing newline stripped), or .err! on EOF.
let console = Console.Open
console.print("Hello!")
console.prompt("Name: ")[name]
name.case {
  .ok name => console.print(name),
  .err! => {},
}
console.close
dec Console.Open : Console

Opens a new Console connected to stdin/stdout.

Http

HTTP request and response types, plus client and server operations.

Request and response bodies are Bytes.Reader<Error> values, so body data can be consumed incrementally.

type Http.Error = String

Error type for HTTP operations (a human-readable message).

Sends an HTTP request and returns the response.

let try url = Url.FromString("https://example.com")
let try (status, headers) body = Http.Fetch(
  ("GET")
  (url)
  (*())
  Bytes.EmptyReader
)
dec Http.Listen : [String] recursive either {
  .incoming(Http.Request, [Http.Response] Try<Http.Error, !>) self,
  .shutdown Try<Http.Error, !>,
}

Http.Listen(address) starts an HTTP server on address, such as "127.0.0.1:8080".

Returns a recursive stream of events:

  • .shutdown result — the server has shut down (e.g. on Ctrl+C or error).
  • .incoming(request, respond) next — a request arrived. Call respond with a Response to send the reply. The result indicates if sending succeeded.
Http.Listen("127.0.0.1:8080").begin.case {
  .shutdown try! => ...,
  .incoming(request, respond) next => {
    let (method, url, headers) body = request
    body.close
    respond((200, *()) Bytes.EmptyReader)
    next.loop
  }
}
Os

Operating system interfaces for paths, files, directories, standard streams, and environment variables.

Most operations return Try<Os.Error, ...>.

type Os.Error = String

Error type for OS operations (a human-readable message).

type Os.Path = iterative@append recursive@parent box choice {
  .absolute => Bytes,
  .append(Bytes) => self@append,
  .name => Bytes,
  .parent => Option<self@parent>,
  .parts => List<Bytes>,
}

A filesystem path value.

  • .name — get the file name component as bytes.
  • .absolute — get the absolute path as bytes.
  • .parts — get all path components as a list of byte sequences.
  • .parent — get the parent path, or .none! if at root.
  • .append(part) — return a path with one more path component.
dec Os.TraverseDir : [Os.Path] Try<Os.Error, recursive either {
  .dir(Os.Path, self) self,
  .end!,
  .file(Os.Path) self,
}>

Recursively traverses a directory tree, returning a nested structure of files and subdirectories sorted by name.

  • .end! — no more entries.
  • .file(path) rest — a file, followed by more entries.
  • .dir(path, children) rest — a subdirectory with its nested contents, followed by more entries.
Sql

SQL database access, backed by sqlx. PostgreSQL, MySQL, and SQLite are selected from the connection URL scheme.

A Database is a linear reference to a shared pool. Query streams and transactions are independent linear leases. .split creates another linear pool handle; .close drops only this handle and never waits for siblings or outstanding leases.

Temporal values travel as text. The portable driver does not decode dedicated temporal column types, so on PostgreSQL and MySQL, cast temporal columns to text when reading (e.g. at::text) and cast text parameters when writing (e.g. $1::timestamptz); SQLite stores temporals as text natively.

type Sql.Error = String

Error type for SQL operations (a human-readable message).

type Sql.Format<a> = box choice {
  .parse(Sql.Value) => Try<Sql.Error, a>,
}

A reusable decoder for one present SQL value.

Required formats report SQL NULL, wrong value variants, and invalid textual temporal values as .err with a human-readable message. Missing columns are handled by Decode, before the format is called.

type Sql.Row = List<(String) Sql.Value>

Ordered result row. Column names are included because most everyday code wants name lookup, but duplicate names are still represented faithfully.

type Sql.Transaction = iterative choice {
  .commit => Try<Sql.Error, !>,
  .execute(String, List<Sql.Value>) => Try<Sql.Error, (Nat) self>,
  .query(String, List<Sql.Value>) => Try<Sql.Error, (List<Sql.Row>) self>,
  .rollback => Try<Sql.Error, !>,
}

A transaction owns one connection. Statement failure consumes the transaction and the implementation rolls back or drops the connection before reporting the error. Successful statements return self. A .commit error means the commit may or may not have taken effect (e.g. when it times out).

type Sql.Value = either {
  .bool Bool,
  .bytes Bytes,
  .float Float,
  .int Int,
  .null!,
  .text String,
}

A portable SQL scalar for parameters and result cells.

This intentionally remains ordinary Par data. Par comparison/display on Value is structural Par behavior, not SQL comparison semantics.

Only PostgreSQL produces .bool result cells; SQLite and MySQL surface stored booleans as integers (AsBool accepts both). Columns declared BOOLEAN on SQLite and MySQL cannot be decoded by the portable driver at all — store booleans in integer columns there.

dec Sql.AsBool : Sql.Format<Bool>

Required boolean format. A check, not a coercion, with one pragmatic exception: .int 0 and .int 1 are accepted as booleans, because SQLite and MySQL surface stored booleans as integers.

Required date-time format: parses zone-free civil text (T or space separator, optional fractional seconds), interpreted in the given zone. Covers text written by SQL itself, such as SQLite's datetime('now').

Required timestamp format: parses zone-aware text into an instant. Accepts RFC 3339, with T or a space as the separator, and Z or any numeric offset — this covers PostgreSQL's timestamptz::text output.

dec Sql.Date : [Time.Zoned] Sql.Value

Renders a zoned date-time as ISO date text, e.g. 2024-07-01, as a .text parameter. Cast in SQL when the backend needs a concrete temporal type.

Renders a zoned date-time as zone-free ISO date-time text, e.g. 2024-07-01T12:34:56, as a .text parameter; sub-second precision is dropped. Cast in SQL when the backend needs a concrete temporal type.

dec Sql.Decode : [Option<Sql.Value>] <a>[Sql.Format<a>] Try<Sql.Error, a>

Decode an optional value with a format. Missing columns return .err. Designed for pipe chains: row->Sql.Field("age")->Sql.Decode(Sql.AsInt)

Look up the first column with this name. .none! means missing column; .some.null! means present SQL NULL. Rows may contain duplicate names; pattern match on Row directly if duplicates matter.

dec Sql.Nullable : <a>[Sql.Format<a>] Sql.Format<Option<a>>

Accept SQL NULL as .none!; otherwise parse with the provided format and wrap the result in .some. Missing columns still error.

Connect and validate connectivity. The URL scheme picks the backend: postgres://..., mysql://..., sqlite://file.db, sqlite::memory:.

SQLite does not create a missing database file by default; open with sqlite://file.db?mode=rwc to create it. An in-memory SQLite database is pinned to a single connection, so drain or cancel its query streams before running further statements.

Renders an instant as RFC 3339 text, as a .text parameter; cast in SQL (e.g. $1::timestamptz) when the backend needs a concrete temporal type.