← Back to AILP Home

Nitpick Streams Architecture

Nitpick utilizes a powerful six-stream architecture that extends the traditional UNIX standard streams. This architecture provides dedicated channels for distinct types of data flow, ensuring clean separation of concerns for command-line tools and system services.

Six-Stream Architecture

Stream FD Purpose Default Behavior
stdin 0 Text Input Reads from console input or piped stdin
stdout 1 Text Output Writes to console output or piped stdout
stderr 2 Error Output Writes to console error output
stddbg 3 Debug Output Writes to stderr if FD 3 is not redirected
stddati 4 Binary Data Input Fails if FD 4 is not redirected
stddato 5 Binary Data Output Fails if FD 5 is not redirected

The addition of stddbg, stddati, and stddato allows programs to cleanly separate diagnostic information from normal error reporting, and to handle binary payloads natively without polluting standard text streams.

Streams as First-Class Values

In Nitpick, streams are opaque handles represented by the int8-> type (or specific stream handles in the compiler's internal representation). They are first-class values and can be freely passed to or returned from functions.

Example: Custom Logger

You can write flexible functions that write to any target stream:

func:log_message = void(int8->:out_stream, string:level, string:msg) {
    print(out_stream, `[&{level}] `);
    println(out_stream, msg);
}

func:main = int32() {
    // Log an error to stderr
    log_message(stderr, "ERROR", "Failed to connect to database");

    // Log debug information to the debug stream
    log_message(stddbg, "DEBUG", "Connection retry attempt 1");

    exit 0;
}

File Streams

In addition to the standard system streams, you can create new streams connected to files on disk.

Opening and Managing

File streams are opened using File.open(path, mode) where mode is "r", "w", or "a".

func:process_data = void() {
    // Open stream
    int8->:f = raw File.open("output.txt", "w");

    // Use stream
    File_write(f, "Processing complete.\\n");

    // Manage lifecycle
    File_flush(f);
    File_close(f);
}

Stream Lifecycle

  1. Open: Acquire the handle using File.open.
  2. Use: Read/write data and manipulate the cursor with File_seek and File_tell.
  3. Close: Always close your file streams with File_close to release system resources. The standard system streams (0-5) do not need to be closed.

Shell Redirection

The true power of the six-stream architecture becomes apparent when used in shell environments. You can independently redirect any of the six streams.

# Standard redirection
./my_program > output.txt 2> error.log

# Redirect debug stream (FD 3) to a separate file
./my_program 3> debug.log

# Read binary data from a file (FD 4) and write to stdout
./my_program 4< input.bin > output.txt

If stddbg (FD 3) is not redirected by the shell, Nitpick automatically falls back to printing debug output to stderr. This ensures debug messages are not lost when running interactively.