An installer for GHC can be downloaded from its home page or as part of the Haskell Platform. For reference, the full documentation is available that can be searched through Hoogle.
In this lab session, we are going to implement the TextIO
monad that emulates some of the console operations, such as reading and writing to a virtual console (represented as a set of buffers) without the IO
monad.
The TextIO
is the following type:
TextIO :: * -> *
For the definition, we are going to employ the following helper types:
type Input = String
type Output = String
where Input
stores the characters for the standard input, and Output
stores the characters for the standard output. The operations of TextIO
shall always read the characters they need from a value of type Input
, and they shall always return their result in an n-tuple that consists of the following:
The value of the computation (of some type a
).
The characters remaining in the input buffer (which is of type of Input
).
The characters produced by the operation (which is of type of Output
).
Hence the TextIO
monad works as follows. Consider the following action:
exampleTIO = do
write "Hi "
s <- readLine
writeLn (s ++ "!")
return 42
This could be then run with the runTextIO
function:
runTextIO :: TextIO a -> Input -> (a, Input, Output)
that then gives the following result:
TextIO*> runTextIO exampleTIO "Mikey\nFreddy\n"
(42,"Freddy\n","Hi Mikey!\n")
Define a type class instance for the Functor
, Applicative
, and Monad
classes, together with the following monad-specific operations.
Printing a string:
write :: String -> TextIO ()
Printing a string with line break. Note that it could be expressed with the help of write
.
writeLn :: String -> TextIO ()
Reading a single character from the input.
readChar :: TextIO Char
Reading a string until line break. Note that it could be even expressed with the help of readChar
.
readLine :: TextIO String
Once the implementation is complete, consider the following refactoring steps:
Implement the TextIO
monad with using the State
monad. The State
could be imported from the Control.Monad.State
module. In this case, the State
might be used for gradually consuming the input.
Express the TextIO
monad as the composition of the State
and Writer
monads. Here, the State
monad might be exploited for consuming the input (as earlier) and the Writer
might be utilized for producing the output. The Writer
monad could be imported from the Control.Monad.Writer
module.
Enjoy the fun! :-)