SagaSaga

File I/O

Reading, writing, and deleting files using the File effect. Errors are represented as an ADT (FileError), not strings, so you can match on specific failure cases.

import Std.File (File, FileError, fs)

fun process : Unit -> Unit needs {File}
process () = {
  let result = File.write! "test_output.txt" "hello from saga!"
  case result {
    Ok () -> ()
    Err _ -> dbg "write failed"
  }

  let result = File.read! "test_output.txt"
  case result {
    Ok content -> dbg content
    Err e -> case e {
      File.NotFound -> dbg "file not found"
      File.PermissionDenied -> dbg "permission denied"
      _ -> dbg "other error"
    }
  }

  let _ = File.delete! "test_output.txt"
  dbg $"file exists after delete?: {File.exists! "test_output.txt"}"

  let missing = File.read! "nonexistent.txt"
  case missing {
    Ok _ -> dbg "unexpected"
    Err File.NotFound -> dbg "correctly got NotFound"
    Err e -> dbg $"wrong error: {debug e}"
  }
}

main () = process () with fs

Output:

hello from saga!
file exists after delete?: False
correctly got NotFound

The fs handler connects the File effect to the real filesystem. In tests, you could provide a handler that operates on an in-memory map instead.