Defer Statements
The defer keyword provides automatic cleanup and resource management in Utah functions. Deferred statements execute automatically when a function exits, regardless of how it exits (normal return, early return, or error).
Basic Syntax
Simple Defer Statement
function processFile(filename: string): void {
  let file = fs.openFile(filename);
  defer file.close();  // Always executes when function exits
  // Process the file...
  if (someCondition) {
    return;  // file.close() still executes
  }
  // More processing...
}  // file.close() executes here too
Generated Bash:
processFile() {
  local filename="$1"
  local _utah_defer_commands_processFile=()
  # Set up cleanup trap
  trap '_utah_cleanup_processFile' RETURN ERR EXIT
  local file
  file=$(fs_openFile "${filename}")
  # Register defer command
  _utah_defer_commands_processFile+=("file_close \"${file}\"")
  # Function body...
  if [ "${someCondition}" = "true" ]; then
    return
  fi
}
_utah_cleanup_processFile() {
  local i
  for ((i=${#_utah_defer_commands_processFile[@]}-1; i>=0; i--)); do
    eval "${_utah_defer_commands_processFile[i]}"
  done
}
Execution Order
LIFO (Last In, First Out)
Deferred statements execute in reverse order of their declaration:
function setupResources(): void {
  defer console.log("Cleanup step 1");
  defer console.log("Cleanup step 2");
  defer console.log("Cleanup step 3");
  console.log("Function body");
}
Output:
Function body
Cleanup step 3
Cleanup step 2
Cleanup step 1
Multiple Defer Statements
function complexCleanup(): void {
  let connection = db.connect();
  defer connection.close();
  let tempDir = "$(mktemp -d)";
  defer "$(rm -rf ${tempDir})";
  // Process file directly without opening a file handle
  let logContent: string = fs.readFile("process.log");
  defer fs.appendFile("cleanup.log", "Cleanup completed\n");
  // Work with resources...
  if (errorCondition) {
    return;  // All three cleanup actions execute in reverse order
  }
}
Common Patterns
Resource Management
function downloadAndProcess(url: string, outputFile: string): void {
  // Create temporary directory
  let tempDir = "$(mktemp -d)";
  defer "$(rm -rf ${tempDir})";
  // Download file
  let downloadPath = "${tempDir}/download.tmp";
  web.download(url, downloadPath);
  defer fs.delete(downloadPath);
  // Process and write to output file
  let processedData: string = processDownloadedData(downloadPath);
  defer fs.writeFile(outputFile, processedData);
  // Process and write...
}
Lock Management
function criticalSection(): void {
  let lockFile = "/tmp/process.lock";
  // Acquire lock
  fs.createFile(lockFile);
  defer fs.delete(lockFile);
  // Critical work that must be protected...
}
Cleanup Notifications
function longRunningTask(): void {
  console.log("Starting long task...");
  defer console.log("Task completed!");
  // Set up progress tracking
  defer console.log("Cleaning up progress tracking");
  // Actual work...
  for (let i = 0; i < 100; i++) {
    // Process item i
  }
}
Error Handling
Defer with Try/Catch
function robustProcessing(): void {
  let resource = acquireResource();
  defer resource.release();  // Always executes, even if exception occurs
  try {
    // Risky operation
    processData(resource);
  } catch (error) {
    console.log("Error occurred: ${error}");
    // defer still executes after catch block
  }
}
Conditional Cleanup
function conditionalSetup(useBackup: boolean): void {
  let primaryResource = setupPrimary();
  defer primaryResource.cleanup();
  if (useBackup) {
    let backupResource = setupBackup();
    defer backupResource.cleanup();  // Only executes if backup was created
  }
  // Work with resources...
}
Advanced Usage
Variable Capture
Variables referenced in defer statements are captured by value at the time the defer is declared:
function variableCapture(): void {
  let message = "Initial";
  defer console.log("Deferred: ${message}");  // Captures "Initial"
  message = "Modified";
  console.log("Current: ${message}");         // Prints "Modified"
}
Output:
Current: Modified
Deferred: Initial
Function Calls in Defer
function complexCleanup(): void {
  let config = loadConfig();
  defer saveConfig(config);  // Function call with argument
  let server = startServer(config.port);
  defer server.stop();       // Method call
  // Server operations...
}
Best Practices
1. Pair Resource Acquisition with Defer
// Good: Immediate defer after acquisition
function goodPattern(): void {
  let file = fs.openFile("data.txt");
  defer file.close();  // Paired immediately
  // Use file...
}
// Avoid: Defer far from acquisition
function avoidPattern(): void {
  let file = fs.openFile("data.txt");
  // Lots of code...
  defer file.close();  // Easy to miss or forget
}
2. Use Defer for All Cleanup
function comprehensiveCleanup(): void {
  // File system cleanup
  let tempFile = fs.createTempFile();
  defer fs.delete(tempFile);
  // Network cleanup
  let connection = net.connect("api.example.com");
  defer connection.close();
  // Process cleanup
  let process = system.startBackground("worker");
  defer process.kill();
}
3. Avoid Complex Logic in Defer
// Good: Simple cleanup calls
defer file.close();
defer connection.disconnect();
// Avoid: Complex logic in defer
defer {
  if (someCondition) {
    // Complex cleanup logic
  }
}  // Not supported - defer only accepts simple statements
Limitations
1. Function Scope Only
Defer statements can only be used inside functions:
// Error: defer outside function
defer console.log("Invalid");  // Compilation error
function validUsage(): void {
  defer console.log("Valid");  // OK
}
2. Simple Statements Only
Defer only accepts simple statements, not complex control structures:
function limitations(): void {
  // Valid defer statements
  defer file.close();
  defer console.log("Done");
  defer cleanup();
  // Invalid defer statements
  defer {  // Error: blocks not supported
    if (condition) {
      doCleanup();
    }
  }
  defer for (let i = 0; i < 10; i++) {  // Error: loops not supported
    cleanup(i);
  }
}
3. No Defer Modification
Once declared, defer statements cannot be cancelled or modified:
function noModification(): void {
  defer cleanup();
  // Cannot cancel or modify the defer
  // The cleanup() call will always execute
}
Implementation Details
Utah implements defer using bash trap handlers that execute when functions exit. The implementation ensures:
- Automatic execution on all exit paths (return, error, normal completion)
 - LIFO ordering through array-based command storage
 - Variable isolation to prevent interference between functions
 - Error resilience so defer execution continues even if individual commands fail
 
The generated bash code uses function-specific arrays to store defer commands and trap handlers to ensure cleanup occurs reliably.