File I/O

Luma treats files as values that reference paths, not as open handles.
Creating a file value never opens the file or touches the filesystem.
All file operations are explicit, atomic, and predictable.

Creating a file reference

f: file = file("data.txt")

This creates a reference to a path.
No file is opened, created, or validated at this point.

You can safely create references to files that do not exist yet.

Reading files

Luma uses type-based dispatch for reading files.
The same method (read()) adapts its behavior based on the declared result type.

Read entire file as text

content: str = file("config.txt").read()
  • Reads the entire file as a string
  • Panics if the file does not exist

Use this when the file is required.

Read entire file as optional text

content: ?str = file("config.txt").read()
if content != nil {
    print(content)
}
  • Returns nil if the file does not exist
  • Never panics

Use this for optional configuration or cache files.

Read file as lines

lines: [str] = file("data.txt").read()
lines.walk(i, line) -> {
    print(line)
}
  • Splits on line breaks
  • Handles both Unix and Windows line endings
  • Panics if the file does not exist

Use this for logs, CSV files, and line-oriented data.

Read file as optional lines

lines: ?[str] = file("data.txt").read()
if lines != nil {
    print(lines.len())
}
  • Returns nil if the file does not exist

Read file as bytes

data: [byte] = file("image.png").read()
print(data.len())
  • Reads raw binary data
  • Panics if the file does not exist

Use this for binary formats, media files, or protocol data.

Read file as optional bytes

data: ?[byte] = file("image.png").read()
if data != nil {
    // process bytes
}

Writing files

Write text (replace content)

file("output.txt").write("Hello, world")
  • Creates the file if it does not exist
  • Replaces existing content if it does

Write bytes (replace content)

data: [byte] = [0x48, 0x65, 0x6C, 0x6C, 0x6F]
file("output.bin").write(data)

Use this for binary output.

Add a line (append with newline)

log: file = file("app.log")
log.add("Application started")
log.add("User logged in")
  • Appends text
  • Automatically adds a newline
  • Creates the file if it does not exist

This is the recommended way to write logs.

Append raw data (no newline)

log.append("[CONTINUED] ")
log.add("Recovered after restart")

Use this only when you explicitly want to control formatting.

File information

Check existence

if file("data.txt").exists() {
    print("File exists")
}

Get file size

size: int = file("large.dat").size()

Returns size in bytes.
Panics if the file does not exist.

Get file name

name: str = file("docs/report.txt").name()  // "report.txt"

Get file extension

ext: str = file("image.png").ext()  // ".png"

Returns an empty string if there is no extension.

Get full path

path: str = file("data/file.txt").path()

Returns the path exactly as provided to file().

Get directory path

dir: str = file("docs/report.txt").dir()  // "docs"

File actions

Create file

file("new.txt").create()
  • Creates an empty file
  • Safe to call even if the file already exists

Delete file

file("temp.txt").delete()

Panics if the file does not exist.

Copy file

file("document.txt").copy("backup/document.txt")
  • Creates destination if needed
  • Overwrites destination if it exists

Move (rename) file

file("old.txt").move("new.txt")

Note: after moving, the original file reference still points to the old path.

Pattern matching with glob

files: [file] = glob("logs/*.log")

Supported patterns include:

  • * – any sequence of characters
  • ? – any single character
  • [abc] – character classes
  • [a-z] – character ranges
  • ** – recursive directory matching

Examples

glob("*.txt")
glob("logs/*.log")
glob("**/*.luma")
glob("data[0-9].txt")

If no files match, an empty list is returned.

Common patterns

Safe configuration loading

fn load_config(path: str) -> str {
    config: ?str = file(path).read()
    if config != nil {
        return config
    }
    return "default=value"
}

Process multiple files

files: [file] = glob("data/*.txt")
files.walk(i, f) -> {
    print(f.name())
}

Ensure file exists

f: file = file("settings.txt")
if !f.exists() {
    f.create()
    f.write("default settings")
}

Planned features

The following methods are planned but not yet implemented:

created() -> date   // file creation time
updated() -> date   // last modification time

These will become available once Luma introduces a date type.

Design notes

  • Files are path references, not open handles
  • All operations are atomic (open → operate → close)
  • Error handling uses optional types instead of exceptions
  • No explicit open/close or streaming APIs are required
  • The common case remains simple, while rare cases stay possible
Last updated on