Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

File System

Final’s filesystem API covers paths, metadata, directories, and whole-file helpers. Sequential streaming I/O (line/byte readers and writers) lives under File Streams — this page does not repeat open/read/write stream factories.

You already know the pieces these APIs lean on:

TypeRole
PathClass-only path-string algebra (join, split, clean, …)
FileExists / info / copy / move / delete; whole-file read/write; name helpers
DirDirectory root value; cwd; mkdir; filesystem iteration
FileInfoMetadata snapshot (name, size, modTime, isDir, …)

Use help File / help Dir / help Path / help FileInfo in the TUI for the full member list.


Path (class-only)

Operands are strings on the data stack. There are no Path instances.

MethodEffectDescription
join( base:s name:s -- s )join with host separator
split( s -- list:StringList )path components (absolute keeps root)
clean( s -- s )drop redundant . / ..
abs( s -- s )absolute path (uses process cwd)
basename( s -- s )leaf without extension
dirname( s -- s )parent directory
extname( s -- s )extension with leading ., or empty
"out" "report.md" Path.join^          // "out/report.md" (host sep)
"a/./b/../c" Path.clean^              // "a/c"
"out/reports/q1.md" Path.basename^    // "q1"
"out/reports/q1.md" Path.extname^     // ".md"
"/var/log/syslog" Path.split^ foreach i $ sp /foreach

FileInfo

Snapshot returned by File.info / Dir.info and by directory iteration. No public new.

PropTypeDescription
pathStringpath for this entry
nameStringleaf name
sizeIntsize in bytes
modTimeDateTimemodification time
isDirBooldirectory?
isSymlinkBoolsymlink?

File (class methods)

Most ops take a path string. Mutators use the modal shape unless noted.

MethodEffectDescription
exists? / fileExists?( path:s -- b )missing → false
info( path:s -- fi:FileInfo true | err:s false )metadata
delete( path:s -- true | err:s false )file or empty directory
move( src:s dst:s -- true | err:s false )rename/relocate; dst is full path; no clobber
copy( src:s dstdir:s -- true | err:s false )copy into directory; keeps leaf name; no clobber
getBaseExt( name:s -- base:s ext:s )leaf split; ext without dot
makeName( base:s ext:s -- name:s )join base+ext; empty ext → base alone
readText / readText?( path:s -- s ) / modalwhole file as String
writeText / writeText?( path:s s -- ) / modalcreate/truncate UTF-8
readBytes / readBytes?( path:s -- bytes ) / modalwhole file as ByteList
writeBytes / writeBytes?( path:s bytes -- ) / modalcreate/truncate bytes

Bare read/write forms panic on I/O error; ? forms are modal. Stream open/read/write: File Streams.

"archive/report.final.md" File.getBaseExt^   // base="report.final" ext="md"
"out/report" "md" File.makeName^             // "out/report.md"
"." File.exists?^
"." File.info^ if >ts t:.name@ $ sp t:.isDir@ $ nl t:drop else $ nl then

"tmp/notes.txt" "Apple" File.writeText?^
"tmp/notes.txt" "tmp/archive" File.copy^
"tmp/archive/notes.txt" "tmp/archive/notes-2.txt" File.move^
"tmp/archive/notes-2.txt" File.delete^

Dir

A Dir is a directory root value (path), not an open OS handle.

KindNameEffectDescription
classnew( path:s -- dir )Dir for path
classcwd( -- dir )process working directory
classsetCwd( path:s -- true | err:s false )change process cwd
classexists?( path:s -- b )path exists?
classinfo( path:s -- fi true | err:s false )metadata
classmkdir( path:s -- true | err:s false )MkdirAll; idempotent if already a dir
propstringStringcanonical path string for this Dir
methoditer( flags:i -- iter )FileSystemIter of FileInfo under this Dir

Iteration flags (class properties on Dir)

Combine with bitwise OR:

FlagMeaning
Dir.IterDirsinclude directories
Dir.IterFilesinclude files (non-dirs)
Dir.IterDeeprecurse
Dir.IterAlldirs + files + deep
"." Dir.new^ >ts
t:.string@ $ nl

"tmp/demo" Dir.mkdir^
"tmp/demo" Dir.new^ >ts
Dir.IterFiles t:.iter^
foreach
  i >xs
  x:.name@ $ nl
  x:drop
/foreach
t:drop

Deep walk (pick a small root — recursive walks can be large):

: filesysIter  ( root:s -- )
  Dir.new^ >ts
  Dir.IterAll t:.iter^
  foreach
    i >ts
      t:.isDir if ">" else " " then
      "%s %9d %v %s %s\n" fmt $
      drop
    t:drop
  /foreach
  t:drop
;

"." filesysIter

Security

Filesystem members are gated by SR.FileRead, SR.FileWrite, and SR.FileSystemRead (exists/info/iter/cwd vs content open). Restricted Sessions may deny them. See Security Restrictions.

Examples on disk

  • examples/file-system/path.fnl
  • examples/file-system/file.fnl
  • examples/file-system/dir.fnl
  • examples/file-system/filesys-iter.fnl