Hello, world! (The IO Monad)

Writing Applications

In most language related classes, we start with a “Hello world!” program.

With 130, we will end with it.












Purity and the Immutability Principle

Haskell is a pure language. Not a value judgment, but a precise technical statement:

The “Immutability Principle”:

  • A function must always return the same output for a given input

  • A function’s behavior should never change









No Side Effects

Haskell’s most radical idea: expression ==> value

  • When you evaluate an expression you get a value and nothing else happens

Specifically, evaluation must not have an side effects

  • change a global variable or

  • print to screen or

  • read a file or

  • send an email or

  • launch a missile.









Purity

Means functions may depend only on their inputs

  • i.e. functions should give the same output for the same input every time.









But… how to write “Hello, world!”

But, we want to …

  • print to screen
  • read a file
  • send an email

A language that only lets you write factorial and fibonacci is … not very useful!

Thankfully, you can do all the above via a very clever idea: Recipe









Recipes

This analogy is due to Joachim Brietner

Haskell has a special type called IO – which you can think of as Recipe

type Recipe a = IO a

A value of type Recipe a is

  • a description of an effectful computations

  • when when executed (possibly) perform some effectful I/O operations to

  • produce a value of type a.












Recipes have No Effects

A value of type Recipe a is

  • Just a description of an effectful computation

  • An inert, perfectly safe thing with no effects.

Cake vs. Recipe

(L) chocolate cake, (R) a sequence of instructions on how to make a cake.

They are different (hint: only one of them is delicious.)

Merely having a Recipe Cake has no effects: holding the recipe

  • Does not make your oven hot

  • Does not make your your floor dirty












Executing Recipes

There is only one way to execute a Recipe a

Haskell looks for a special value

main :: Recipe ()

The value associated with main is handed to the runtime system and executed

Baker Aker

The Haskell runtime is a master chef who is the only one allowed to cook!












How to write an App in Haskell

Make a Recipe () that is handed off to the master chef main.

  • main can be arbitrarily complicated

  • will be composed of many smaller recipes












Hello World

putStrLn :: String -> Recipe ()

The function putStrLn

  • takes as input a String
  • returns as output a Recipe ()

putStrLn msg is a Recipe () when executed prints out msg on the screen.

main :: Recipe ()
main = putStrLn "Hello, world!"

… and we can compile and run it

$ ghc --make hello.hs
$ ./hello
Hello, world!












QUIZ: Combining Recipes

Next, lets write a program that prints multiple things:

main :: IO ()
main = combine (putStrLn "Hello,") (putStrLn "World!")

-- putStrLn :: String -> Recipe ()
-- combine  :: ???

What must the type of combine be?

{- A -} combine :: () -> () -> ()
{- B -} combine :: Recipe () -> Recipe () -> Recipe ()
{- C -} combine :: Recipe a  -> Recipe a  -> Recipe a
{- D -} combine :: Recipe a  -> Recipe b  -> Recipe b
{- E -} combine :: Recipe a  -> Recipe b  -> Recipe a
















Using Intermediate Results

Next, lets write a program that

  1. Asks for the user’s name using
    getLine :: Recipe String
  1. Prints out a greeting with that name using
    putStrLn :: String -> Recipe ()

Problem: How to pass the output of first recipe into the second recipe?
















QUIZ: Using Yolks to Make Batter

Suppose you have two recipes

crack     :: Recipe Yolk
eggBatter :: Yolk -> Recipe Batter

and we want to get

mkBatter :: Recipe Batter
mkBatter = crack `combineWithResult` eggBatter

What must the type of combineWithResult be?

{- A -} Yolk -> Batter -> Batter
{- B -} Recipe Yolk -> (Yolk  -> Recipe Batter) -> Recipe Batter
{- C -} Recipe a    -> (a     -> Recipe a     ) -> Recipe a
{- D -} Recipe a    -> (a     -> Recipe b     ) -> Recipe b
{- E -} Recipe Yolk -> (Yolk  -> Recipe Batter) -> Recipe ()
















Looks Familiar

Wait a bit, the signature looks familiar!

combineWithResult :: Recipe a -> (a -> Recipe b) -> Recipe b

Remember this

(>>=)             :: Result a -> (a -> Result b) -> Result b












Recipe is an instance of Monad

In fact, in the standard library

instance Monad Recipe where
  (>>=) = {-... combineWithResult... -}

So we can put this together with putStrLn to get:

main :: Recipe ()
main = getLine >>= \name -> putStrLn ("Hello, " ++ name ++ "!")

or, using do notation the above becomes

main :: Recipe ()
main = do name <- getLine
          putStrLn ("Hello, " ++ name ++ "!")












EXERCISE

  1. Compile and run to make sure its ok!

  2. Modify the above to repeatedly ask for names.

  3. Extend the above to print a “prompt” that tells you how many iterations have occurred.



















Monads are Amazing

Monads have had a revolutionary influence in PL, well beyond Haskell, some recent examples

  • Error handling in go e.g. 1 and 2

  • Asynchrony in JavaScript e.g. 1 and 2

  • Big data pipelines e.g. LinQ and TensorFlow








A Silly App to End CSE 130

Lets write an app called moo inspired by cowsay

A Command Line App

moo

moo works with pipes

Thanks, and good luck for the final!