Error Handling¶
11.1 Result Type¶
Commands in expression context return a result type for explicit error handling.
# Commands return Result<str, Error>
let result = cat nonexistent.txt
match result with
| Ok content -> println $"Content: {content}"
| Error e -> println $"Error ({e.code}): {e.message}"
# Built-in Error type
type Error = {
code: int # Exit code
message: str # Error description
}
# Result type definition
type result<T, E> =
| Ok of T
| Error of E
# Checking results
let result = fetchData url
if isOk result then
process (unwrap result)
else
log $"Failed: {getError result}"
11.2 Error Propagation with ?¶
The ? operator propagates errors up the call stack (similar to Rust).
# Propagate errors automatically
let processFile path =
let content = cat $path? # Returns Error if cat fails
let parsed = parseJson content? # Returns Error if parse fails
transform parsed # Returns Ok result
# Equivalent explicit version
let processFile path =
match cat $path with
| Error e -> Error e
| Ok content ->
match parseJson content with
| Error e -> Error e
| Ok parsed -> Ok (transform parsed)
# Chain multiple fallible operations
let deployApp =
buildProject?
runTests?
packageArtifacts?
uploadToServer?
notifyTeam
# Use in pipelines
let data =
fetchUrl url?
|> parseJson?
|> extractField "data"?
|> validate?
11.3 Try-With Expression¶
Catch and handle errors explicitly.
# Basic try-with
let result = try
let data = fetchData url?
let parsed = parseJson data?
processData parsed
with
| { code = 404; _ } ->
DefaultData.empty
| { code = c; message = m } when c >= 500 ->
log $"Server error: {m}"
Error { code = c; message = m }
| e ->
Error e
# Try with specific error patterns
let config = try
loadConfig configPath?
with
| { message = m } when contains m "not found" ->
println "Config not found, using defaults"
DefaultConfig
| { message = m } when contains m "permission" ->
println $"Cannot read config: {m}"
exit 1
| e ->
println $"Unexpected error: {e.message}"
exit 1
# Try-finally for cleanup
let result = try
let handle = openFile path
processFile handle
finally
closeFile handle
11.4 Option Type for Missing Values¶
Use option for values that might not exist.
# Option type
type option<T> = Some of T | None
# Functions returning Option
let find predicate list =
match filter predicate list with
| [] -> None
| [x; _...] -> Some x
let lookup key map =
match filter (fun (k, _) -> k == key) map with
| [(_, v)] -> Some v
| _ -> None
# Using options
match findUser id with
| Some user -> greet user.name
| None -> echo "User not found"
# Option combinators — module-qualified syntax
let email =
findUser id
|> Option.map (fun u -> u.email)
|> Option.defaultValue "no-email@example.com"
let result =
findUser id
|> Option.bind (fun u -> u.manager) # Returns option<User>
|> Option.map (fun m -> m.email)
# Option combinators — method-style syntax
let email = (findUser id).map (fun u -> u.email)
let name = (findUser id).defaultValue "anonymous"
let manager = (findUser id).bind (fun u -> u.manager)
# Optional chaining (syntactic sugar)
let email = user?.profile?.email ?| "default@example.com"
# The above is equivalent to:
let email =
match user with
| None -> "default@example.com"
| Some u ->
match u.profile with
| None -> "default@example.com"
| Some p ->
match p.email with
| None -> "default@example.com"
| Some e -> e
11.5 Exit Codes (Bash Compatibility)¶
Traditional exit code handling is fully supported.
# Access last exit code
someCommand
if $? == 0 then
echo "Success"
else
echo "Failed with code $?"
# Exit with code
exit 0 # Success
exit 1 # General error
exit 127 # Command not found
# Return from function with value
let checkFile path =
if test -f $path then return 0 else return 1
# Check command success in conditions
if grep -q pattern file.txt then echo "Found"
# Boolean commands
if test -f $file && test -r $file then cat $file
See also: Pattern Matching | Control Flow | Command Execution