REPL
- Allows writing e.g.
functionOf do ...
instead offunctionOf $ do
. void
is a convenient function, which turnsIO a
toIO ()
. Used here because we don't care about the output of the repl, and in fact that output is never reached, because the repl runs a loop indefinitely.forever
takes a value of typem X
(wherem
has aMonad
constraint on it) and loops it:forever mX = mX >> forever mX
. It's a clean way to write a for-loop.- Get a line from the user.
- Parse the user's input, using
parse
from theParser
module. - Get the current state of the board.
hiding
is convenient for avoiding namespace conflicts.pure
is likereturn
, but only requires theApplicative
typeclass. It can be used almost everywhere thatreturn
is used, but is strictly more general, because aMonad
constraint implies anApplicative
constraint.catchError
, here used in infix form, stops an error percolating to the top level. This is useful here, because an uncaught error would haltmain
, and so exit the repl.- Neither
return
norpure
is a keyword, and a block ofdo-notation
does not need to end with it. All that is needed at the end of the "do-block" is a value of typem a
(for the monadm
in question), anddisplayLine result
has that type. catchError
chooses what to do with the error it catches, and one option is to throw it again. It does this for errors includingExit
, in order to exit the repl on ":q".runReplWithBoard
is the function responsible for "unpacking" the monadic value into something simpler. This is sometimes referred to as "running the side effects" of a program.flip
is a useful function that takes a function of typeX -> Y -> Z
and flips the argument order to give a function of typeY -> X -> Z
. It is often useful when writing in pointfree style.- It is often useful to lift the type
IO X
to the more abstractMonadIO m => m X
, which is whatliftIO
does.
Analysis¶
This module is responsible for producing the actual runnable program (of type IO ()
) that wraps up the whole system.
main
¶
```haskell
main :: IO () main = void $ runReplWithBoard $ displayLine "Welcome!\n\n" >> forever do
line <- requestLine "> "
let instruction = parse line
board <- get
result <-
case instruction of
Left (ParseError err) -> pure err
Left (ReplError err) -> pure err
Right instr -> evaluate instr
`catchError` ( \case
ReplError txt -> pure txt
err -> throwError err
)
displayLine result
```
```haskell
main :: IO () main = runReplWithBoard $ displayLine "Welcome!\n\n" >> loop where
loop = do
line <- requestLine "> "
let instruction = parse line
board <- get
result <-
case instruction of
Left (ParseError err) -> pure err
Left (ReplError err) -> pure err
Right instr -> evaluate instr
`catchError` ( \case
ReplError txt -> pure txt
err -> throwError err
)
displayLine result
loop
```
```haskell
main :: IO () main = do runReplWithBoard $ displayLine "Welcome!\n\n" >> forever do
line <- requestLine "> "
let instruction = parse line
board <- get
result <-
case instruction of
Left (ParseError err) -> pure err
Left (ReplError err) -> pure err
Right instr -> evaluate instr
`catchError` ( \case
ReplError txt -> pure txt
err -> throwError err
)
displayLine result
pure ()
```
Last update:
February 9, 2023
Created: January 8, 2023
Created: January 8, 2023