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.
(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
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 complicatedwill 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 ()
= putStrLn "Hello, world!" main
… 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 ()
= combine (putStrLn "Hello,") (putStrLn "World!")
main
-- 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
- Asks for the user’s
name
using
getLine :: Recipe String
- 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
= crack `combineWithResult` eggBatter mkBatter
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 ()
= getLine >>= \name -> putStrLn ("Hello, " ++ name ++ "!") main
or, using do
notation the above becomes
main :: Recipe ()
= do name <- getLine
main putStrLn ("Hello, " ++ name ++ "!")
EXERCISE
Compile and run to make sure its ok!
Modify the above to repeatedly ask for names.
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
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
works with pipes