March Docs

File

File module: filesystem I/O. All I/O functions except exists return Result(value, FileError). Use with_lines / with_chunks for streaming. The file handle is closed when the callback returns and if the callback panics — these wrappers delegate to the runtime try_finally primitive to guarantee cleanup even on unhandled exceptions. (each_line / each_chunk close the handle only on normal return.)

Types

ptypeFileErrorFileError = NotFound(String) | Permission(String) | IsDirectory(String) | NotEmpty(String) | IoError(String)#
ptypeFileKindFileKind = RegularFile | Directory | Symlink | OtherKind#
ptypeFileStatFileStat = FileStat(Int, FileKind, Int, Int)#

FileStat uses a positional constructor Pattern match: FileStat(size, kind, modified, accessed)

ptypeSeqSeq(a) = Seq(a)#

Seq wraps a church-encoded fold closure. Single-pass.

Functions

fnappendappend(path, data) do file_append(path, data) end#

Append data to the end of path, creating the file if it does not exist. Returns Ok(:ok) or Err(FileError).

Examples

File.append("app.log", "started\n")
fncopycopy(src, dest) do file_copy(src, dest) end#

Copy the contents of src to dest, creating or overwriting dest. Returns Ok(:ok) or Err(FileError) (reported as IoError(msg)).

fndeletedelete(path) do file_delete(path) end#

Delete the file at path. Returns Ok(:ok), or Err(FileError) if it does not exist or cannot be removed. Use Dir.rmdir / Dir.rm_rf for directories.

fneach_chunkeach_chunk(path, size, f)#

Call f(chunk) for each chunk of at most size bytes of path, for side effects. Returns Ok(:ok) or Err(FileError) if the file cannot be opened. The file is NOT closed if f panics (use with_chunks for that).

fneach_lineeach_line(path, f)#

Call f(line) for each line of path (terminators stripped), for side effects. Returns Ok(:ok), or Err(FileError) if the file cannot be opened. Unlike with_lines, the file is NOT closed if f panics.

Examples

File.each_line("names.txt", fn name -> println("hello " ++ name))
fnexistsexists(path)#

True if any filesystem entry exists at path, including directories. Use Dir.exists to test specifically for directories. Never fails.

Examples

if File.exists("out.log") do File.delete("out.log") else Ok(:ok) end
fnmtimemtime(path)#

The modification time of path as a Unix timestamp in seconds (Ok(mtime)), or the FileError from stat.

fnreadread(path)#

Read the whole file at path into a String. Returns Ok(contents) or Err(FileError), e.g. NotFound(path), Permission(path) or IsDirectory(path).

Examples

match File.read("config.toml") do
  Ok(text) -> parse(text)
  Err(NotFound(p)) -> defaults()
  Err(e) -> panic("cannot read config")
end
fnread_linesread_lines(path)#

Read the file at path and split it on \n. Returns Ok(lines) without the newline characters; ALL trailing empty lines are dropped (so a final newline, or several, adds no empty element, and an empty file gives Ok([])). A \r before each \n is kept. Errors as for read.

Examples

-- file contents "a\nb\n\n"
File.read_lines("notes.txt")   -- Ok(["a", "b"])
fnrenamerename(src, dest) do file_rename(src, dest) end#

Rename (move) src to dest, replacing dest if it exists. Returns Ok(:ok) or Err(IoError("<src>: <reason>")).

fnstatstat(path)#

Metadata for path: Ok(FileStat(size_bytes, kind, modified, accessed)) where kind is a FileKind and the times are Unix timestamps in whole seconds. Returns Err(NotFound(path)) if nothing exists there.

Examples

match File.stat("data.bin") do
  Ok(FileStat(size, RegularFile, _, _)) -> size
  _ -> 0
end
fnwith_chunkswith_chunks(path, size, callback)#

Like with_lines, but the Seq yields raw chunks of at most size bytes (the last one may be shorter). Returns Ok(callback_result) or Err(FileError). The file is closed even if callback panics.

Examples

File.with_chunks("blob.bin", 65536, fn chunks ->
  Seq.fold(chunks, 0, fn (n, c) -> n + string_byte_length(c)))
fnwith_lineswith_lines(path, callback)#

Open path, pass the callback a single-pass Seq(String) of its lines (line terminators stripped), then close the file. Returns Ok(callback_result), or Err(FileError) if the file cannot be opened. Lines are read lazily as the Seq is folded, so memory stays bounded. The Seq is single-pass: consuming it twice yields nothing the second time, and it must not escape the callback (the file is closed after). try_finally guarantees the fd is closed even if callback panics.

Examples

File.with_lines("big.log", fn lines ->
  Seq.fold(lines, 0, fn (n, line) ->
    if String.contains(line, "ERROR") do n + 1 else n end))
fnwritewrite(path, data) do file_write(path, data) end#

Write data to path, creating the file or truncating an existing one. Returns Ok(:ok) or Err(FileError) (e.g. the parent directory does not exist).

Examples

File.write("out.txt", "hello\n")