REPL
| REPL.hs | |
|---|---|
- Allows writing e.g.
functionOf do ...instead offunctionOf $ do. voidis a convenient function, which turnsIO atoIO (). 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.forevertakes a value of typem X(wheremhas aMonadconstraint 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
parsefrom theParsermodule. - Get the current state of the board.
hidingis convenient for avoiding namespace conflicts.pureis likereturn, but only requires theApplicativetypeclass. It can be used almost everywhere thatreturnis used, but is strictly more general, because aMonadconstraint implies anApplicativeconstraint.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
returnnorpureis a keyword, and a block ofdo-notationdoes 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 monadmin question), anddisplayLine resulthas that type. catchErrorchooses 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".runReplWithBoardis the function responsible for "unpacking" the monadic value into something simpler. This is sometimes referred to as "running the side effects" of a program.flipis a useful function that takes a function of typeX -> Y -> Zand 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 Xto the more abstractMonadIO m => m X, which is whatliftIOdoes.
Analysis¶
This module is responsible for producing the actual runnable program (of type IO ()) that wraps up the whole system.
main¶
main :: IO ()
main =
void $ -- (2)!
runReplWithBoard $
displayLine "Welcome!\n\n" >> forever do -- (3)!
line <- requestLine "> " -- (4)!
let instruction = parse line -- (5)!
board <- get -- (6)!
result <-
case instruction of
Left (ParseError err) -> pure err -- (8)!
Left (ReplError err) -> pure err
Right instr -> evaluate instr
`catchError` ( \case -- (9)!
ReplError txt -> pure txt
err -> throwError err -- (11)!
)
displayLine result -- (10)!
main :: IO ()
main =
runReplWithBoard $
displayLine "Welcome!\n\n" >> loop where
loop = do -- (3)!
line <- requestLine "> " -- (4)!
let instruction = parse line -- (5)!
board <- get -- (6)!
result <-
case instruction of
Left (ParseError err) -> pure err -- (8)!
Left (ReplError err) -> pure err
Right instr -> evaluate instr
`catchError` ( \case -- (9)!
ReplError txt -> pure txt
err -> throwError err -- (11)!
)
displayLine result -- (10)!
loop
main :: IO ()
main = do
runReplWithBoard $
displayLine "Welcome!\n\n" >> forever do -- (3)!
line <- requestLine "> " -- (4)!
let instruction = parse line -- (5)!
board <- get -- (6)!
result <-
case instruction of
Left (ParseError err) -> pure err -- (8)!
Left (ReplError err) -> pure err
Right instr -> evaluate instr
`catchError` ( \case -- (9)!
ReplError txt -> pure txt
err -> throwError err -- (11)!
)
displayLine result -- (10)!
pure ()
Last update:
January 17, 2023
Created: January 8, 2023
Created: January 8, 2023