Created
January 6, 2015 01:51
-
-
Save jameshfisher/1d735b5267e8f848b280 to your computer and use it in GitHub Desktop.
Non-monadic IO in Haskell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module NonMonadicIO where | |
import GHC.Base (returnIO, bindIO) | |
-- The IO API: | |
-- | |
-- returnIO :: a -> IO a | |
-- bindIO :: IO a -> (a -> IO b) -> IO b | |
-- Here's a program which does nothing: | |
skip :: IO () | |
skip = returnIO () | |
-- Here's a program which does nothing in a more complicated way: | |
alsoSkip :: IO () | |
alsoSkip = bindIO skip (\() -> skip) | |
-- To do useful things, Haskell gives us specific actions like: | |
-- | |
-- getLine :: IO String | |
-- putStrLn :: String -> IO () | |
-- Here's hello world: | |
hello :: IO () | |
hello = putStrLn "Hello, world!" | |
-- Here's a program which gets a string from the user and echoes it back: | |
echo :: IO () | |
echo = bindIO getLine putStrLn | |
-- Here's a program which asks politely first: | |
politeEcho :: IO () | |
politeEcho = bindIO (putStrLn "Please enter a string: ") (\() -> echo) | |
-- Here's a "combinator" which does one program then another: | |
semicolon :: IO () -> IO () -> IO () | |
semicolon p1 p2 = bindIO p1 (\() -> p2) | |
rudeEcho :: IO () | |
rudeEcho = putStrLn "Enter a string." `semicolon` | |
(bindIO getLine (\l -> | |
putStrLn "Here's your string." `semicolon` | |
putStrLn l | |
)) | |
-- We can run a whole list of programs: | |
runAll :: [IO ()] -> IO () | |
runAll [] = returnIO () | |
runAll (p:ps) = p `semicolon` runAll ps | |
echo3 :: IO () | |
echo3 = runAll [putStrLn "Please enter a string: ", echo, putStrLn "Done!"] | |
-- We can run a program in a never-ending loop: | |
loop :: IO () -> IO () | |
loop prog = prog `semicolon` loop prog | |
-- Here's an echo server: | |
echoServer :: IO () | |
echoServer = loop echo3 | |
-- We can run a program in a loop but let it say when to stop: | |
while :: IO Bool -> IO () | |
while prog = bindIO prog (\quit -> if quit then returnIO () else while prog) | |
echoWithStop :: IO () | |
echoWithStop = while (bindIO (putStrLn "Enter a string (q to quit):") (\() -> | |
(bindIO getLine (\l -> | |
if l == "q" | |
then return True | |
else (bindIO (putStrLn "Here's your string:") (\() -> | |
(bindIO (putStrLn l) (\() -> | |
returnIO False | |
)))))))) | |
-- A more general semicolon which passes on the result of the second program: | |
semicolon' :: IO a -> IO b -> IO b | |
semicolon' p1 p2 = bindIO p1 (\_ -> p2) | |
stateMachine :: (s -> IO s) -> s -> IO s | |
stateMachine step init = bindIO (step init) (stateMachine step) | |
counter :: IO Int | |
counter = stateMachine (\ctr -> getLine `semicolon'` print ctr `semicolon'` returnIO (ctr+1)) 0 | |
discard :: IO a -> IO () | |
discard p = p `semicolon'` skip | |
main :: IO () | |
main = discard counter |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment