Zulip Chat Archive

Stream: new members

Topic: main issue in Functional Programming in Lean, 6.1.3


Garrison Venn (Nov 23 2025 at 16:00):

In section 6.1.3 the use of ReaderT was used to modify the code referenced in 6.1.2. Because no main was provided, the implication was to use the main in 6.1.2. However when comparing the two outputs one gets the following in a test directory.

The section 6.1.2 version gives:

├── gtest/
   ├── adding
   ├── adding.out
   ├── inner/
      ├── one
      ├── two
   ├── origAdding
   ├── using
   ├── using.out

The section 6.1.3 version gives:

gtest/
    adding
    adding.out
    inner/
       one
       two
    origAdding
    using
    using.out

To to fix this, I hacked configFromArgs to set the default Config.currentPrefix to be Config.preFile.

From:

def configFromArgs : List String  Option Config
  | [] => some {} -- both fields default
  | ["--ascii"] => some {useASCII := true}
  | _ => none

To:

def Config.prefixFixed (cfg : Config) :=
  {cfg with currentPrefix :=
    if cfg.currentPrefix.length == 0 then
      cfg.preFile
    else
      cfg.currentPrefix}

def configFromArgs : List String  Option Config
  | [] => some (({} : Config).prefixFixed) -- both fields default
  | ["--ascii"] => some ({useASCII := true} : Config).prefixFixed
  | _ => none

I may of course be missing something. However if such a similar change is necessary, I thought the tutorial should make both sections 6.1.2 and 6.1.3 have the same output. Sorry for the noise if this is incorrect. Also the main implementation, as used in the section 6.1.3 version, is using the ReaderT.run function because of the type change of dirTree in 6.1.3, but ReaderT.run is not discussed though is the same as 6.1.2's ConfigIO.run.

My full hacked code of 6.1.3 follows:

structure Config where
  useASCII : Bool := false
  currentPrefix : String := ""
deriving Repr

instance : ToString Config where
  toString c := s!"\{useASCII := {c.useASCII}"
                ++ s!", currentPrefix := {c.currentPrefix}}"

def Config.preFile (cfg : Config) :=
  if cfg.useASCII then "|--" else "├──"

def Config.preDir (cfg : Config) :=
  if cfg.useASCII then "|  " else "│  "

abbrev ConfigIO (α : Type) : Type := ReaderT Config IO α

def showFileName (file : String) : ConfigIO Unit := do
  IO.println s!"{(← read).currentPrefix} {file}"

def showDirName (dir : String) : ConfigIO Unit := do
  IO.println s!"{(← read).currentPrefix} {dir}/"

def Config.inDirectory (cfg : Config) : Config :=
  {cfg with currentPrefix := cfg.preDir ++ " " ++ cfg.currentPrefix}

def doList [Applicative f] : List α  (α  f Unit)  f Unit
  | [], _ => pure ()
  | x :: xs, action =>
    action x *>
    doList XS action

inductive Entry where
  | file : String  Entry
  | dir : String  Entry

def toEntry (path : System.FilePath) : IO (Option Entry) := do
  match path.components.getLast? with
  | none => pure (some (.dir ""))
  | some "." | some ".." => pure none
  | some name =>
    pure (some (if ( path.isDir) then .dir name else .file name))

def dirLT (e1 : IO.FS.DirEntry) (e2 : IO.FS.DirEntry) : Bool :=
  e1.fileName < e2.fileName

partial def dirTree (path : System.FilePath) : ConfigIO Unit := do
  match  toEntry path with
    | none => pure ()
    | some (.file name) => showFileName name
    | some (.dir name) =>
      showDirName name
      let contents  path.readDir
      withReader (·.inDirectory)
        (doList (contents.qsort dirLT).toList fun d =>
          dirTree d.path)

def usage : String :=
  "Usage: doug [--ascii]
Options:
\t--ascii\tUse ASCII characters to display the directory structure"

--
-- Hacked configFromArgs
--

def Config.prefixFixed (cfg : Config) :=
  {cfg with currentPrefix :=
    if cfg.currentPrefix.length == 0 then
      cfg.preFile
    else
      cfg.currentPrefix}

def configFromArgs : List String  Option Config
  | [] => some (({} : Config).prefixFixed) -- both fields default
  | ["--ascii"] => some ({useASCII := true} : Config).prefixFixed
  | _ => none

--
-- Referenced main from 6.1.2
--

def main (args : List String) : IO UInt32 := do
    match configFromArgs args with
    | some config =>
      --dbg_trace config
      -- This is using ReaderT.run vs the 6.1.2 version of ConfigIO.run
      (dirTree ( IO.currentDir)).run config
      pure 0
    | none =>
      IO.eprintln s!"Didn't understand argument(s) {" ".intercalate args}\n"
      IO.eprintln usage
      pure 1

Garrison Venn (Dec 01 2025 at 13:54):

@David Thrane Christiansen I know you don't have a lot of time, but would you be willing to look at this? I know this is a bit more involved so is probably something I got incorrect.


Last updated: Dec 20 2025 at 21:32 UTC