// ------------ And now for a very different monad, IO --------- // // Read https://cosc59.gitlab.io/hs/functors-in-math-vs-haskell.txt first // if you haven't yet. // Let's see IO's getArgs and getLine in action sergey@snowball hs % cat Haq.hs -- From https://wiki.haskell.org/How_to_write_a_Haskell_program -- -- Copyright (c) 2006 Don Stewart - http://www.cse.unsw.edu.au/~dons/ -- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html) -- -- in hugs89, run with :l "Haq.hs" then :main me import System.Environment -- for getArgs -- | 'main' runs the main program -- | main :: IO () main = getArgs >>= print . haqify . head haqify s = "Haq! " ++ s ghci> import System.Environment ghci> :t getArgs getArgs :: IO [String] --- a list of Strings, each argument a separate String ghci> :t getLine --- one line at a time getLine :: IO String ghci> :l Haq.hs [1 of 2] Compiling Main ( Haq.hs, interpreted ) Haq.hs:14:37: warning: [GHC-63394] [-Wx-partial] In the use of ‘head’ (imported from Prelude, but defined in GHC.Internal.List): "This is a partial function, it throws an error on empty lists. Use pattern matching, 'Data.List.uncons' or 'Data.Maybe.listToMaybe' instead. Consider refactoring to use "Data.List.NonEmpty"." | 14 | main = getArgs >>= print . haqify . head | ^^^^ Ok, one module loaded. // Note the warning: the nicest thing about >>= pipelines is that they // don't throw exceptions. Using a function that does is contrary to that style. ghci> head [] :39:1: warning: [GHC-63394] [-Wx-partial] In the use of ‘head’ (imported from Prelude, but defined in GHC.Internal.List): "This is a partial function, it throws an error on empty lists. Use pattern matching, 'Data.List.uncons' or 'Data.Maybe.listToMaybe' instead. Consider refactoring to use "Data.List.NonEmpty"." *** Exception: Prelude.head: empty list HasCallStack backtrace: error, called at libraries/ghc-internal/src/GHC/Internal/List.hs:2036:3 in ghc-internal:GHC.Internal.List errorEmptyList, called at libraries/ghc-internal/src/GHC/Internal/List.hs:96:11 in ghc-internal:GHC.Internal.List badHead, called at libraries/ghc-internal/src/GHC/Internal/List.hs:90:28 in ghc-internal:GHC.Internal.List head, called at :39:1 in interactive:Ghci1 // :main runs the 'main' function from the loaded file, without arguments: ghci> :main "Haq! *** Exception: Prelude.head: empty list HasCallStack backtrace: error, called at libraries/ghc-internal/src/GHC/Internal/List.hs:2036:3 in ghc-internal:GHC.Internal.List errorEmptyList, called at libraries/ghc-internal/src/GHC/Internal/List.hs:96:11 in ghc-internal:GHC.Internal.List badHead, called at libraries/ghc-internal/src/GHC/Internal/List.hs:90:28 in ghc-internal:GHC.Internal.List head, called at Haq.hs:14:37 in main:Main // To run 'main' with some arguments, use ":main arg1 arg2 ..." ghci> :main foo bar baz "Haq! foo" // There is another method to supply arguments to getArgs: ghci> :set args foo bar baz ghci> getArgs ["foo","bar","baz"] // ...but calling :main without arguments overrides it: ghci> :set args foo bar baz ghci> :main "Haq! *** Exception: Prelude.head: empty list HasCallStack backtrace: error, called at libraries/ghc-internal/src/GHC/Internal/List.hs:2036:3 in ghc-internal:GHC.Internal.List errorEmptyList, called at libraries/ghc-internal/src/GHC/Internal/List.hs:96:11 in ghc-internal:GHC.Internal.List badHead, called at libraries/ghc-internal/src/GHC/Internal/List.hs:90:28 in ghc-internal:GHC.Internal.List head, called at Haq.hs:14:37 in main:Main // Suggested Exercise: make this safe for no arguments given! // getLine works similarly to wrap the nasty interactions with the OS: sergey@snowball hs % cat read-string.hs main = getLine >>= \s -> putStrLn ("Echo " ++ s ) >> putStrLn ("Done") sergey@snowball hs % ghc read-string.hs [1 of 2] Compiling Main ( read-string.hs, read-string.o ) [2 of 2] Linking read-string sergey@snowball hs % ./read-string Hello Echo Hello Done // Observe the types: ghci> :i putStr putStr :: String -> IO () -- Defined in ‘GHC.Internal.System.IO’ ghci> :t putStrLn putStrLn :: String -> IO () -- the empty tuple, no arguments passed on ghci> :t getLine getLine :: IO String ghci> :t getArgs getArgs :: IO [String] // Note that >> is like >>= but the function to the right of it takes no // arguments: ghci> say_smth = putStr "He" >> putStr "llo\n" ghci> say_smth ghci> :t say_smth say_smth :: IO () // It's merely another syntactic convenience. We could instead supply // a lambda that discards its argument: ghci> say_smth = putStr "He" >>= \x -> putStr "llo\n" ghci> say_smth Hello --------- in Prelude '98: ------------- class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a m >> k = m >>= \_ -> k --- // <=== right here, defines >> via >>= fail s = error s -------- end Prelude --------------- // Now let's handle some inputs: sergey@snowball hs % cat read.hs main = do putStrLn "Please enter a Double:" inpStr <- getLine let inpDouble = (read inpStr)::Double putStrLn ("Twice " ++ show inpDouble ++ " is " ++ show (inpDouble * 2)) sergey@snowball hs % ghc read.hs [1 of 2] Compiling Main ( read.hs, read.o ) [2 of 2] Linking read sergey@snowball hs % ./read Please enter a Double: 2.0 Twice 2.0 is 4.0 sergey@snowball hs % ./read Please enter a Double: 2.5 Twice 2.5 is 5.0 // The 'do' looks like imperative syntax but it's actually syntactic sugar over >>= sergey@snowball hs % cat read1.hs main = putStrLn "Please enter a Double:" >> getLine >>= \s -> let inpDouble = (read s)::Double in putStrLn ("Twice " ++ show inpDouble ++ " is " ++ show (inpDouble * 2)) sergey@snowball hs % ghc read1.hs [1 of 2] Compiling Main ( read1.hs, read1.o ) [2 of 2] Linking read1 sergey@snowball hs % ./read1 Please enter a Double: 4.5 Twice 4.5 is 9.0 // But let is also syntactic sugar over lambdas: sergey@snowball hs % cat read2.hs main = putStrLn "Please enter a Double:" >> getLine >>= \s -> (\d -> putStrLn ("Twice " ++ show d ++ " is " ++ show (d * 2))) ((read s)::Double) // Note that the parens around (read s)::Double are super-important. // Remove them and see what happens! // Hint: the explicit type declaration---our first ever in Haskell!---applies // to the whole expression unless properly bracketed. sergey@snowball hs % ghc read2.hs sergey@snowball hs % ./read2 Please enter a Double: 5.6 Twice 5.6 is 11.2