haskell programming fundamentals: learn pure functional ... · 9 more haskell expressions 500.0 * 2...

39
1 Haskell Programming Fundamentals: Learn Pure Functional Thinking Instructor: Barry Burd, [email protected] Copyright © 2019, Barry Burd What to install: Haskell Platform, https://www.haskell.org/platform/ Haskell for Everyone IDE, https://haskellforeveryone.com Haskell Expressions 5 + 4 -- ghci shows you the value of the expression (+) 5 4 -- parentheses turn infix to prefix (+) (5 4) -- error message addFive = (+) 5 -- partial evaluation addFive 4 div 5 2 5 `div` 2 -- back ticks turn prefix to infix doubleme x = x + x doubleme 5 :t 5 -- a ghci command (not a Haskell statement) :t (+) -- :t is short for :type Haskell’s Predefined Types (https://www.haskell.org/onlinereport/haskell2010/haskellch6.html)

Upload: others

Post on 20-May-2020

13 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

1

Haskell Programming Fundamentals: Learn Pure Functional Thinking

Instructor: Barry Burd, [email protected]

Copyright © 2019, Barry Burd

What to install:

Haskell Platform, https://www.haskell.org/platform/

Haskell for Everyone IDE, https://haskellforeveryone.com

Haskell Expressions

5 + 4 -- ghci shows you the value of the expression

(+) 5 4 -- parentheses turn infix to prefix

(+) (5 4) -- error message

addFive = (+) 5 -- partial evaluation

addFive 4

div 5 2

5 `div` 2 -- back ticks turn prefix to infix

doubleme x = x + x

doubleme 5

:t 5 -- a ghci command (not a Haskell statement)

:t (+) -- :t is short for :type

Haskell’s Predefined Types (https://www.haskell.org/onlinereport/haskell2010/haskellch6.html)

Page 2: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

2

More Haskell Expressions

add = (+)

mult = (*)

add 5 18

mult 5 18

add (5 18)

(add 5) 18

(add 5) 18

x = 21

y = 13

z = add x

z y

succ 6

succ succ 6

(succ succ) 6

succ (succ 6)

3 + 5 * 10

succ (succ (succ 6))

succ $ succ 6

succ $ succ $ succ 6

Haskell's precedence and associativity table

(https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-820004.4.2)

Page 3: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

3

More Haskell Expressions

a 3 == 5 3 /= 5 -- not equal

(True && True) || (5 == 7)

not True

add (mult 5 4) $ mult 7 9 -- $ has the lowest precedence

(add (mult 5 4)) $ mult 7 9 -- $ is like enclosing here to the end

-- of the line in parentheses

"Hello"

:info succ

"Hello" ++ ", there!" -- list concatenation

[5,7,8,12,13]

[5, 7, "Hello"] -- error message

[1..10]

[] -- Behold! The empty list

[1,4,6] ++ [9,8,3]

['H', 'e', 'l', 'l', 'o']

mylist = [33, 11, 32, 1, 7]

head mylist -- Don't forget:

-- head is a list entry but

tail mylist -- tail is a list of entries

(head mylist) ++ (tail mylist)

[head mylist] ++ (tail mylist)

(head mylist):(tail mylist) -- the colon operator

head $ tail mylist

init mylist -- init and last

last mylist

x = init mylist

y = last mylist

newstuff = x ++ [y]

newstuff

newstuff == mylist

firstplusone x = head x + 1

firstplusone mylist

firstplustoneV2 = succ . head -- the composition operator

firstplustoneV2 mylist

firstplusoneV3 x = succ head x

firstplusoneV3 x = succ $ head x

firstplusoneV3 mylist

(succ . head) mylist

succ . head mylist

succ . head $ mylist

last . init $ mylist

nexttolast = last . init

nexttolast mylist

listoflists = [[1,2], [5,6,7], [8,9,10]]

head listoflists

Page 4: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

4

putStrLn "Hello"

putStrLn 3

putStrLn "3"

show 3

putStrLn (show 3) -- don't forget show

putStrLn $ show 3

print 3

Exercise Set 1

Solutions are in the back of this manual.

1. Given a list named b, write a line of code to find the number that's one more than the first number in

the list. For example, if b is [5, 19, 21, 6], write a line of code that outputs 6.

2. Define a new function (named f2) that does what you did in Question 1.

3. Write a line of code to find the sum of the first two numbers in a list. For example, if b is [5, 19, 21, 6],

the output of this line of code is 24.

4. Define a new function (named f4) that does what you did in Question 3.

5. Write a line of code to create a list consisting of the first two numbers in a list. For example, if b is

[5, 19, 21, 6], the output of this line of code is [5, 19].

6. Define a new function (named f6) that does what you did in Question 5.

7. Write a line of code to create a list consisting of all but the first two numbers in a list. For example, if

b is [5, 19, 21, 6], the output of this line of code is [21, 6].

8. Explain why tail tail b is not a valid solution for Question 7.

9. If b is [5, 19, 21, 6], what's wrong with the following expression? tail (head b)

10. Write a point-free definition of the function that you defined in Question 7. (Remember: A point-free

definition has no variables in it.)

11. You're trying to create a point-free definition of the function that you defined in Question 2. What's

wrong with the following definition? f2 = head + 1

12. (More difficult) Write a point-free definition of the function that you defined in Question 2.

Page 5: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

5

More Haskell Expressions

mylist = [5, 19, 21, 6, 9, 15, 0]

f x = tail $ tail x

f mylist

g = tail . tail

g mylist

:t True

:t 5

:t 5.0

:t "Hello"

:t 'a'

:t (+)

:t tail

:t head

:t g

:t 4

:t (show 4)

read "38" :: Int -- opposite of show

read "38" -- error

read ("38" :: Int) -- error

[4,5,6]

[4,5, "x"]

mylist

head mylist

tail mylist

5: $ tail mylist

5:(tail mylist)

take 5 [1..] -- lazy evaluation

[1.0, 1.25..2.0]

take 10 [1..]

[1..] -- has to be interrupted

(8, "Hello", True)

mytuple = (8, "Hello", True) -- tuples are heterogeneous

fst mytuple

mytuple = (8, "Hello")

fst mytuple -- fst and snd for 2-tuples only

snd mytuple

x = 10

inRange y = if y < 5 then "small" else "big" –- if..then..else

-- is an expression

inRange x

inRange y = if y < 5

:set +m

inRange y = if y < 5

then "small"

else "big"

inRange 16

Page 6: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

6

z <- getLine

z

inRange z

inRange (read z)

inRange $ read z

mysentence <- getLine

mysentence

read mysentence

-- This is a comment

status x = if head x < last x

then "last is bigger"

else (if head x > last x

then "head is bigger"

else "equal")

status [3,4,5]

status [5,4,3]

status [5,3,2,1,5]

status x = if head x < last x then "last is bigger" else if head x >

last x then "head is bigger" else "equal"

status [3,4,5]

status [4,3,1]

status [1,1]

fst (2,5)

snd (2,5)

fst (2, "Hello")

mytupple = (1, 4,2,7)

mytupple

thd (a, b, c, d) = c

thd mytupple

thd (1,2,3,4,5)

thd (_, _, c, _) = c -- underscore wildcard

thd (a, a, c, a) = c

thd (_, _, c, _) = c

thd mytupple

take 5 [2,4,6,3,4,7,9,4,0]

mylist = [2,4,6,3,4,7,9,4,0]

take 5 mylist

take 5 [1..]

drop 5 mylist -- drop

take 5 mylist ++ drop 5 mylist

(take 5 mylist ++ drop 5 mylist) == mylist

10:mylist

head mylist

tail myList

tail mylist

splitAt n list = (take n list, drop n list)

splitAt 3 [1,6,9,8,1,2,5]

makeList a = [a]

Page 7: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

7

makeList 7

mylist

add1 = (+)

add1 x = x + 1

add1 6

map add1 mylist -- map

isEven n = n % 2 == 0

isEven n = (n % 2 == 0)

isEven n = (n mod 2 == 0)

isEven 5

isEven n = ((n mod 2) == 0)

isEven 5

isEven n = (n `mod` 2 == 0)

isEven 5

areEven list = map isEven list

areEven mylist

mylist

filter isEven mylist -- filter

isSmall x == x < 5

isSmall x == (x < 5)

isSmall x = (x < 5)

isSmall x = x < 5

map isSmall mylist

filter isSmall mylist

mylist

:info foldl

foldl (+) 0 mylist

mylist

4,7,9,4,0]

foldl (+) 10 mylist -- foldl and foldr

foldl (*) 1 mylist

mylist = [3,5,7,9, 2,10,1]

mylist

linear x = 3*x + 7

map linear mylist

map (\x -> 3*x + 7) mylist -- lambda expression

map (\x -> 10*x - 4*x**x) mylist

(\x y -> x + 2*y) 4 8

emp1 = ("Joe", 10000.00)

emp2 = ("Jane", 30000.00)

emp3 = ("Mary", 5000.00)

emp4 = ("Ed", 25000.00)

emps = [emp1, emp2, emp3, emp4]

500 + 10000.00

500.00 + 10000.00

name emp = fst emp

name emp1

salary emp = snd emp

Page 8: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

8

salary emp2

map name emps

map salary emps

map (\emp -> salary emp + 500.00) emps

tenth (_, _, _, _, _, _, _, _, _, a) = a

tenth (1,2,3,4,5,6,7,8,9,10)

tenth (1,2,3,4,5,6,7,8,9,54)

map (\emp -> "**" ++ name emp ++ "**") e

foldl (+) 0 $ map salary emps

foldl (++) "" $ map name emps

foldl (\x y -> x ++ " " ++ y) "" $ map name emps

foldl (\x y -> x ++ " " ++ y ++ " ") "" $ map name emps

numbers = [1,2,5]

foldl (+) 0 numbers

foldr (+) 0 numbers

foldl (-) 0 numbers -- subtraction isn't associative

foldr (-) 0 numbers

Exercise Set 2

Here are some employees: emp1 = ("Joe", 10000.00)

emp2 = ("Jane", 30000.00)

emp3 = ("Mary", 5000.00)

emp4 = ("Ed", 25000.00)

emps = [emp1, emp2, emp3, emp4]

1. How can you get an employee's name and salary?

2. How can you get all names; get all salaries?

Here are examples of the use of lambda expressions: (\x -> x * 2 + 3) 7

map (\x -> x + 5) [3,6,2]

3. Use a lambda expression to make a list of (salary, name) tuples.

4. Find the sum of all salaries.

5. Find the salaries less than or equal to 20000.00.

6. Add 500.00 to any salary that's less than or equal 20000.00.

7. Find the sum of salaries that are increased by the previous question.

8. Find the sum of the increases from the previous two questions.

9. Find the sum of all new salaries.

Page 9: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

9

More Haskell Expressions

500.0 * 2

500.0 * length [2,3]

:t 2

500.00 * fromIntegral (length [2,3]) -- need type conversion

:t 500.00

5 / 2

div 5 2

5 `div` 2

5.0 / length [3,4]

5.0 / fromIntegral (length [3,4])

5.0 / fromInteger (length [3,4])

fib 0 = 0 ; fib 1 = 1 ; fib n = fib (n-1) + fib (n-2) -- recursion

:t fib

fib 5

fib 6

fib 0

nth 0 list = head list ; nth n list = nth (n-1) $ tail list

nth 5 [3,2,6,4,7,8,0]

nth 0 (x:xs) = x ; nth n (x:xs) = nth (n-1) xs

nth 5 [3,2,6,4,7,8,0]

nth 0 (x:_) = x ; nth n (_:xs) = nth (n-1) xs

nth 5 [3,2,6,4,7,8,0]

contains k [] = False ; contains k (x:xs) = k == x || contains k xs

contains 8 [1,2,4,6,3]

contains 8 [1,2,4,8,6,3]

rev [] = [] ; rev [a] = [a] ; rev (x:xs) = rev xs ++ [x]

rev "Barry"

rev [1,7,3,5,8,2]

rev2 [] = [] ; rev2 (x:xs) = rev xs ++ [x]

rev2 [2,4,5]

rev2 [3]

Exercise Set 3

1. Zip the elements of two lists of equal length together.

2. Produce a list with n identical elements.

3. Merge two sorted lists a la MergeSort.

4. Flip every two elements (every two adjacent ones).

5. Check if a list is sorted (in increasing order).

6. Find the smallest element in a list.

7. Remove the smallest element from a list (using the previous function).

8. Do a Selection Sort (using the previous two functions).

Page 10: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

10

Creating types from existing types:

type ISBN = Int -- type synonyms

type Title = String

type Author = String

type Authors = [Author]

data Book =

Book {isbn :: ISBN, title :: Title, authors :: Authors} deriving (Show)

-- data type declaration

title2 (Book _ t _) = t

algOfProgramming =

Book 9780135072455 "Algebra of Programming" ["Richard Bird", "Oege de Moor"]

javaForDummies = Book 1118407806 "Java For Dummies" ["Barry Burd"]

type Price = Double

type BookRecord = (Book, Price)

javaRecord = (javaForDummies, 29.95)

Here are some expressions that use the definitions:

algOfProgramming

data Student = Student String Double

student1 = Student "Joe" 2.00 -- creating a value of a data type

student2 = Student "Bukowski" 0.00

data Sale = Purchase String Double

:type (+)

:t (+)

:t Student

:t Purchase

student1

data Student = Student String Double deriving (Show)

student1 = Student "Joe" 2.00

student2 = Student "Bukowski" 0.00

student1

name Student n _ = n

name (Student n _) = n

name student2

isbn algOfProgramming

:t isbn

title algOfProgramming

:t title

: Book -> Title

title2 algOfProgramming

head $ authors algOfProgramming

Page 11: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

11

head "Hello"

head $ head [[2,3], [5,7]]

head $ head ["Hello", "Goodbye", "Go away"]

rec1 = (javaForDummies, 4000.00)

:t rec1

head (authors (fst rec1))

head $ authors $ fst rec1

rec1

Exercise set 4

A Block has three Double values (length, width, and height).

Note: In Haskell, length is the name of a function that returns the

number of elements in a list, so it's safest for you not to try to

reuse that name.

1. Create a list consisting of a few Blocks.

2. Create a function that takes a list of Blocks and returns a list consisting

of the volumes of the Blocks.

3. Create a function that takes a list of Blocks and returns the average of the

volumes of the Blocks.

Note: To divide a Double value by an Int value, do something like this:

theDoubleValue / (fromIntegral theIntValue)

4. Create a function that takes a list of Blocks and returns a list consisting

of similarly shaped Blocks, each having volume 1.

Notes:

To take a number to a power, use the ** symbol. For example, x**(1/3) stands for

the cube root of x (approximately).

Let's say you have a Block whose sides have sizes 3, 4, 5. Then the cube root of the

volume is (3 * 4 * 5)**(1/3) and a similarly shaped Block with volume 1 has sides

3/((3 * 4 * 5)**(1/3)) , 4/((3 * 4 * 5)**(1/3)) , 5/((3 * 4 * 5)**(1/3)).

Exercise Set 5

Create code that makes an Item type.

An Item value has a name (such as "Shirt") and a price (such as 20.00).

Create code that makes a Customer type.

A Customer has a name and a creditCardNumber.

Create a Purchase type. A Purchase has a Customer and a list of items.

In each of the following exercises, you can use functions that you defined in earlier exercises. For

example, for Exercise 5, you can use any of the functions that you defined in Exercises 1, 2, 3 and 4.

Page 12: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

12

0. Create a list of two purchases, named purchasesForToday and show the total of all purchases.

1. Recall that a Purchase contains one or more Items, and each Item has a price. Create a function

that takes a purchase and finds the total amount of that purchase.

2. From your purchasesForToday list, create a list of purchases valued at more than 100.00.

3. From your purchasesForToday list, create a list of customers who have purchased more than

100.00 in goods.

4. Create a list of the names of customers who have purchased more than 100.00 in goods.

5. Create a list of strings of the following kind: "Dear Mary, You've spent more than $100.00."

6. Create a list of strings of the following kind: "Dear Mary, You've purchased $125.2314

in goods." Only customers with more than $100.00 in purchases get this message. Remember that

you can't concatenate a number (such as 125.2314) with a String unless you convert the number

into a String. You do this with the show function, as follows: show 125.2314 ++ " is a lot of money."

7. Modify the function that you created in Exercise 6. But this time, display numbers with only two digits

to the right of the decimal point. For example, "Dear Mary, You've purchased $125.23 in

goods." Here's how you can display a number with only two digits to the right of the decimal point:

Add the following line to the beginning of your program:

import Text.Printf (printf)

Then you can do the following:

x = 3.1415926535

(printf "%.2f" x) :: String

"3.14"

Note that an expression such as

(printf "%.2f" x) :: String

evaluates to a String. So you can do something like this:

"The value of PI is " ++ (printf "%.2f" x) :: String

"The value of PI is 3.14"

Page 13: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

13

I/O (and guards):

import System.IO

fib n = if n == 1 || n == 2

then 1

else fib (n - 1) + fib (n - 2)

-- Here comes a guard:

fibList n

| n == 1 = [1]

| n == 2 = [1, 1]

| otherwise = let prevList = fibList(n - 1)

newElement = last prevList + last (init prevList)

in prevList ++ [newElement]

-- Some simple I/O:

main = do

putStr "Enter a number: "

hFlush stdout

x <- getLine

print $ fibList (read x)

Exercise Set 6:

Create a function that returns True if a list has two adjacent identical elements. Otherwise, the

function returns False. For example, when it acts on [1,2,2] the function returns True; when it

acts on [2,1,2] the function returns False. And when it acts on [2], the function returns False.

Page 14: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

14

Algebraic types:

data Color = Red | Orange | Yellow | Green | Blue | Indigo | Violet

deriving (Eq, Ord, Show, Bounded, Enum) -- use existing type classes

lastColor = maxBound :: Color -- double colon is required

firstColor = minBound :: Color

colorsStartingWith color =

color:(if ((succ color) /= lastColor)

then colorsStartingWith (succ color)

else [succ color])

colorsToList = colorsStartingWith firstColor

reverseColors = reverse colorsToList

colorsEndingWith color =

color:(if ((pred color) /= firstColor)

then colorsEndingWith (pred color)

else [pred color])

colorsToReverseList = colorsEndingWith lastColor

colorOrder = [firstColor .. lastColor]

reverseColorOrder = [lastColor, pred lastColor .. firstColor]

intAverage n1 n2 = ceiling (((fromIntegral n1) + (fromIntegral n2)) / 2 )

paintMix c1 c2 = toEnum (intAverage (fromEnum c1) (fromEnum c2)) :: Color

main = do

putStrLn $ show (colorsStartingWith Red)

putStrLn $ show colorsToList

putStrLn $ show reverseColors

putStrLn $ show reverseColorOrder

putStrLn $ show colorsToReverseList

Page 15: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

15

Types within types:

type CardHolder = String

type CardNumber = String

type Address = [String]

type CustomerID = Int

data BillingInfo =

CreditCard {cardNumber :: CardNumber,

cardHolder :: CardHolder,

address :: Address}

| CashOnDelivery

| Invoice {customerID :: CustomerID}

deriving (Show)

data Color =

Red | Orange | Yellow | Green | Blue | Indigo | Violet

deriving (Eq, Ord, Show, Bounded, Enum)

billInfo1 = CreditCard "1111222233334444" "Bob" ["111 Main Street", "NJ"]

billInfo2 = CashOnDelivery

billInfo3 = Invoice 12345

billInfo4 = CreditCard {address = ["111 Madison Avenue", "Madison"],

cardHolder = "Ellen", cardNumber = "1111333344445555"}

billInfo5 = CreditCard "5555444433332222" "Moe" []

billInfo6 = CreditCard "8888777766665555" "Larry"

["220 Harvey Lane", "San Franciso", "CA"]

billInfo7 = CreditCard "5555333377778888" "Fred" ["Parts Unknown"]

color1 = pred Green

color2 = succ Green

color3 = minBound :: Color

color4 = maxBound :: Color

value1 = toEnum 3 :: Color

value2 = fromEnum Yellow

-- case expression is for pattern matching

whatToDo billInfo =

case billInfo of

CreditCard num holder address -> "Process " ++ num ++ " for " ++ holder

CashOnDelivery -> "Get payment at customer's front door"

Invoice id -> "Send bill to customer " ++ show id

whatToDo2 (CreditCard num holder address) =

"Process " ++ num ++ " for " ++ holder

whatToDo3 (CreditCard num holder address)

| length address == 0 = "Process " ++ num ++ " for " ++ holder

| length address == 1 = "Process " ++ num ++ " for " ++ holder

++ " at " ++ head address

Page 16: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

16

| otherwise = "Process " ++ num ++ " for " ++ holder

++ " at " ++ head address ++ ", "

++ last address

isBig x

| x > 100 = True

| otherwise = False

main = do

putStrLn $ "whatToDo billInfo1: " ++ whatToDo billInfo1

putStrLn $ "whatToDo3 billInfo5: " ++ whatToDo3 billInfo5

putStrLn $ "whatToDo3 billInfo6: " ++ whatToDo3 billInfo6

putStrLn $ "whatToDo3 billInfo7: " ++ whatToDo3 billInfo7

A parameterized type:

data Thing = A|B|C

instance Show Thing where

show A = "a"

show B = "b"

show C = "c"

data List a = Cons a (List a)

| Nil

deriving (Show)

list0 = Cons 0 Nil

list1 = Cons 1 list0

list2 = Cons 2 list1

data List' = Cons' Int List' | Nil'

list0' = Cons' 0 Nil'

list1' = Cons' 1 list0'

list2' = Cons' 2 list1'

showWithoutBrackets Nil' = ""

showWithoutBrackets (Cons' n list') =

show n ++ "," ++ showWithoutBrackets list'

instance Show List' where

show list' = "[" ++ init (showWithoutBrackets list') ++ "]"

main = do

putStrLn $ show list2

putStrLn $ show A

putStrLn $ show list2'

Page 17: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

17

More I/O:

import System.IO

main = do

hSetBuffering stdout NoBuffering

putStr "Enter a number: "

string1 <- getLine

--let number1 = (read string1 :: Double)

let number1 = read string1

putStr "Enter another number: "

string2 <- getLine

--let number2 = (read string2 :: Double)

let number2 = read string2

putStrLn $ "The sum is " ++ show (number1 + number2)

Create a type class instance:

import Text.Printf

data Purchase = Purchase {name :: String, price :: Double}

instance Show Purchase where

show p = "You paid " ++ printf "%.2f" (price p) ++ " for the " ++ (name p)

instance Eq Purchase where

p1 == p2 = (name p1) == (name p2)

purchase1 :: Purchase

purchase1 = Purchase "TV" 300.00

oneDollarMore :: Purchase -> Purchase

oneDollarMore (Purchase n p) = Purchase n (p + 1)

Exercise Set 7:

1. Define a type named Units. The Units type has two values, Feet and Meters.

2. Define a type named Length. Each length has two values: a number (which is a Double) and a

number of units (which is of type Units).

3. Make your Length type an instance of the Show class.

4. Define a class named Convertible. If something is Convertible, it must have a toUS function.

Remember: When you define the Convertible class, you don't give a formula for the toUS function,

you only declare the existence of a toUS function for Convertible types. In this code, you'll declare

the toUS function's existence this way:

toUS :: a -> a

Page 18: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

18

This means that, if you have a value of type a, and you apply the toUS function to that value, the

function will return a value of type a.

5. Declare your Length type to be an instance of the Convertible class. To do so, you must define

what toUS means for a Length value. (Of course, it depends on which Units the Length value has.)

6. Declare your Length type to be an instance of Haskell's Eq class. For example, Length 1 Feet

== Length 1 Feet, and Length 3.28 Feet == Length 1 Meters. Use the toUS

function to define equality for lengths.

7. Define a type named Locale. The Locale type has three values, US, France, and England.

8. Define a type named Currency. Each Currency value has two things: a Locale and a number

(which is a Double). For example, I might have Currency US 10.00 in my pocket.

9. Make Currency be an instance of Haskell's Show class.

10. Make Currency be an instance of your Convertible class. To do so, you must define what

toUS means for a Currency value. (Of course, it depends on which Locale the Currency value

has.)

11. Make Currency be an instance of Haskell's Eq class. For example, Currency 1 US =

Currency 1 US, and Currency 1 England == Currency 1.25 US. Use the toUS

function to define equality for currency values.

12. Define a type named Scale. The Scale type has two values: Fahrenheit and Celsius.

13. Define a type named Temperature. Each Temperature has a numeric value and a Scale.

14. Make your Temperature type an instance of Haskell's Show class.

15. Make your Temperature type an instance of your Convertible class (with Fahrenheit as

the US temperature).

16. Make your Temperature type an instance of Haskell's Eq class. Use the toUS function to define

equality for temperatures.

17. Define a type named HowManyPeople. Each HowManyPeople value has a number and a

Locale. For example,

numberVotingInUSin2016 = HowManyPeople 130000000 US

18. Here are population figures for the three locales:

population US = 322762018

population England = 55040000

population France = 64668129

Make your HowManyPeople type be an instance of the Convertible class. When you convert from

one HowManyPeople value to another, you keep the percentage of the country's population the same

Page 19: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

19

(or nearly the same). For example, half the US population is 161381009, and half of France's

population is 32334064. So

toUS (HowManyPeople 32334064 France) is HowManyPeople 161381009 US

19. Make your HowManyPeople type an instance of Haskell's Eq class. Use the toUS function to

define equality for HowManyPeople values.

Page 20: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

20

The Maybe monad:

sqrtMaybe x = if x >= 0

then Just (x**(0.5))

else Nothing

recipMaybe 0 = Nothing

recipMaybe x = Just (1 / x)

f x = sqrtMaybe x >>= recipMaybe

fNew x = return x >>= sqrtMaybe >>= recipMaybe

p x = if x == Nothing then "Error" else "Not error"

f' x = do

y <- sqrtMaybe x

recipMaybe y

f''' x = (sqrtMaybe >> recipMaybe) x :: Maybe Double

f'''' x = do

sqrtMaybe x

recipMaybe x

main = do

putStrLn "\n"

putStrLn $ show $ f 4

putStrLn $ show $ f 0

putStrLn $ show $ f (-1)

putStrLn ""

putStrLn $ show $ fNew 4

putStrLn $ show $ fNew 0

putStrLn $ show $ fNew (-1)

putStrLn ""

putStrLn $ show $ f' 4

putStrLn $ show $ f' 0

putStrLn $ show $ f' (-1)

putStrLn ""

putStrLn $ show $ f''' 4

putStrLn $ show $ f''' 0

putStrLn $ show $ f''' (-1)

putStrLn ""

putStrLn $ show $ f'''' 4

putStrLn $ show $ f'''' 0

putStrLn $ show $ f'''' (-1)

putStrLn ""

Page 21: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

21

Define a type class instance:

type Name = String

type Rating = Double

data Hotel = Hotel {name :: Name, rating :: Rating} deriving (Show)

data OverallQuality = Good | Marginal | Bad deriving Show

class Evaluate a where

evaluate :: a -> OverallQuality

instance Evaluate Hotel where

evaluate x = if rating x > 3.0 then Good

else if rating x < 3.0 then Bad

else Marginal

ritz = Hotel "Ritz" 5.0

dump = Hotel "Dump" 1.0

ok = Hotel "OK" 3.0

main = do

print $ evaluate ritz

print $ evaluate dump

print $ evaluate ok

Exercise Set 8:

You arrive at a restaurant at 6pm, but you don't get seated until 6:25pm. You might say that your arrivalTime is 600 but your seatedTime is 625. In this case, your waiting time is 25.

1. Write a function named waitingTime. The function takes two Int values

(arrivalTime and seatedTime) and returns the customer's waiting time. [Assume

that a time such as 6:00pm is represented by the Int value 600.)

2. If the input values arrivalTime and seatedTime are incorrect, it doesn't make

sense to calculate the waiting time. For example, one customer answers a survey by writing that they arrived at 5:30pm and were seated at 5:00pm. That's bad input. Write a function named maybeWaitingTime. The function takes two Int values (arrivalTime and seatedTime) and returns a Maybe Int value. The Int value is the customer's waiting time.

3. Write a function named maybeServiceWasOK. This function takes an Int value (the

waiting time) and returns a Maybe Bool value. The Bool value is True for a waiting

Page 22: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

22

time of beteen 0 and 30 minutes, and False for a waiting time that's more than 30 minutes..

4. Create a function that binds the maybeWaitingTime and maybeServiceWasOK

functions together. The function's input is an arrivalTime and a seatedTime. The

function's output is a Maybe Bool value.

5. Create a function named maybeShowBoolean. The maybeShowBoolean function

takes a Bool value and returns a Maybe String value. When the Bool value is True, the String value is "Good Service". When the Bool value is False, the

String value is "Bad Service".

6. Create a function that binds the maybeWaitingTime, maybeServiceWasOK, and maybeShowBoolean functions together. The function's input is an arrivalTime

and a seatedTime. The function's output is a Maybe String value.

Page 23: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

23

Another I/O example:

prompt :: String -> IO String

prompt x = do

putStrLn x

number <- getLine

return number

accum currentList = do

rawNum <- getLine

let num = read rawNum :: Int in

if num /= 0

then accum $ currentList ++ [num]

else print currentList

main :: IO ()

main = accum []

Yet another I/O example:

accum'' current f test myRead = do

string <- getLine

let num = myRead string in

if test num

then accum'' (f num current) f test myRead

else print current

numberOfEs str =

sum $ map (\char -> if char == 'e' || char == 'E' then 1 else 0) str

main = do

putStrLn "Enter several Int values, the last of which is 0 ..."

accum'' [] (\num current -> current ++ [num :: Int]) (/= 0) read

putStrLn "Enter several Int values, the last of which is 0 ..."

accum'' 0 (\num current -> current + (num :: Int)) (/= 0) read

putStrLn "Enter several Int values, the last of which is 0 ..."

accum'' 10000 (\num current -> min current (num :: Int)) (/= 0) read

putStrLn "Enter True True ... True False: "

accum'' 0 (\bool current -> current + 1) id read

putStrLn "That's the number of times you wrote True."

putStrLn "Enter several words, the last of which is STOP ..."

accum'' 0 (\str current -> current + numberOfEs str) (/= "STOP") id

Page 24: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

24

Creating an instance of Read:

class MyClass a where

f :: a -> a

instance MyClass Int where

f n = n + 1

data MyString = MyString String

instance MyClass MyString where

f (MyString s) = MyString $ s ++ "*"

instance Read MyString where

readsPrec _ str = [(MyString (str ++ "!"), "")]

{-

readsPrec :: Read a => Int -> String -> [(a, String)]

The Int argument is operator precedence of the enclosing context

Each tuple is of the form (parsed value, remaining string)

readsPrec returns a list containing only one tuple or,

in the no parse case, an empty list.

read s = fst (head (readsPrec 0 s))

-}

instance Show MyString where

show (MyString s) = s ++ "@"

main = do

n <- getLine

let m = (read n) :: Int

putStrLn $ show $ f m

str <- getLine

let mstr = (read str) :: MyString

putStrLn $ show $ f mstr

Page 25: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

25

Generating values randomly:

import System.Random

{-

randomR takes a tuple of bounds and a generator. It returns a

random value and a new generator.

random runs randomR with the appropriate default arguments

randoms generates a list of random values using the bounds

expressed in random

newStdGen's value is a monad containing a generator

-}

main = do

print $ randomR ('a', 'z') (read "1" :: StdGen)

g <- newStdGen

print $ randomR ('a', 'z') g

print $ randomR ('a', 'z') g

print $ randomR ('a', 'z') (snd (randomR ('a', 'z') g))

print . take 10 $ (randomRs ('a', 'z') g)

print . take 5 $ (randomRs (1,100 :: Int) g)

Exercise Set 9:

A room has a room number, a capacity, and an occupancy (all Int values).

1. Repeatedly prompt the user for a room number and a room capacity. Stop prompting the user when the

user enters the value -1 for the room number. This gives you a list of rooms.

2. Repeatedly prompt the user for a number of guests (the number of guests who want a room).

Immediately after the user enters a number of guests, look for an empty room with the capacity for at

least that number of guests. Produce a new list of rooms in which the occupancy of room that you found

is equal to the number of guests who wanted a room. Display a "Sorry" message if there are no rooms

with sufficient capacity. Stop repeating when all rooms are filled.

Exercise Set 10:

Write a program that randomly generates a number from 1 to 10. Then the program repeatedly asks the user to

guess a number. The program stops running when the user guesses the number that was generated.

Page 26: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

26

Exercise Solutions

Exercise Set 1

1. head b + 1

2. f2 x = head x + 1

3. head b + head (tail b)

4. f4 x = head x + head (tail x)

5. [head b, head (tail b)]

6. f6 x = [head b, head (tail b)]

7. tail (tail b)

8. If you write tail tail b, then you start by working from left to right, trying to do tail tail

before you involve b in the evaluation. But there's no such thing as tail tail because then you're

trying to take the "tail of tail" but you can't take the tail of a function. You can only take the tail of a list.

9. In that expression, you first take the head of b, which is going to be a single element; namely, the

element 5. This element 5 is not a list of any kind. So you can't take the tail of that single element. You

can only take the tail of a list.

10. f10 = tail . tail

11. In the expression head + 1, you're trying to add two things that can't be added together. The

first, head, is a function and the second, 1, is a number.

12. f2 = ((+) 1) . head

Explanation: To turn the + operator into a function that can go before its argument, put the + sign in

parentheses, getting (+).

Then, remember that a function takes only one argument. So ((+) 1) is a new function that adds 1 to

anything you apply it to. In class, we used the example (max 7)

In that example, (max 7) is a new function that returns the maximum of 7 and anything that you

apply (max 7) to. So (max 7) 3 is 7, and (max 7) 9 is 9.

Finally, the composition ((+) 1) . head applies the head function first, and then applies the

((+) 1) function.

By the way, you could also write this function the following two ways: f2 = (1+) . head

f2 = (+1) . head

Page 27: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

27

Exercise Set 2

emp1 = ("Joe", 10000.00)

emp2 = ("Jane", 30000.00)

emp3 = ("Mary", 5000.00)

emp4 = ("Ed", 25000.00)

emps = [emp1, emp2, emp3, emp4]

1. name x = fst x

salary x = snd x

2. map name emps

map salary emps

3. map (\x -> (salary x, name x)) emps

4. foldl (+) 0.0 $ map salary emps

5. filter (\x -> salary x <= 20000.00) emps

6. map (\x -> salary x + 500.00) (filter (\x -> salary x <= 20000.00) emps)

7. foldl (+) 0.0

(map (\x -> salary x + 500.00) (filter (\x -> salary x <= 20000.00) emps))

8. foldl (+) 0.0

(map (\x -> 500.00) (filter (\x -> salary x <= 20000.00) emps))

9. foldl (+) 0.0 $ map

(\x -> if salary x <= 20000.00 then salary x + 500.00 else salary x) emps

Page 28: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

28

Exercise Set 3

1. zip [] _ = [] ; zip _ [] = [] ; zip (x:xs) (y:ys) = x : y : zip xs ys

zip "Hello" "Barry"

2. replicate 0 x = []

replicate n x = x:(replicate (n-1) x)

replicate 7 2

3. merge [] list = list

merge list [] = list

merge (x:xs) (y:ys) = if x >= y then y : (merge (x:xs) ys)

else x : (merge xs (y:ys))

merge [1,4,7,9] [3,5,8,9]

merge [1,4,7,9] [3,5,8,9, 10, 22]

4. flip [] = []

flip [a] = [a]

flip (x:y:xs) = y:x:(flip xs)

flip [1,2,3,4,5,6]

flip [1,2,3,4,5,6,7]

5. isSorted [] = True

isSorted [a] = True

isSorted [a,b] = a <= b

isSorted (x:y:xs) = x <= y && isSorted (y:xs)

isSorted [2,3,5,2,6,8]

isSorted [0,1,2,4,6,8]

6. small [a] = a

small (x:xs) = if x <= small xs then x else small xs

small [4,2,7,5,8]

small [4,2,7,5,8, 0]

small [0, 4,2,7,5,8, 5]

7. removeSmall [a] = []

removeSmall (x:xs) = if small (x:xs) == x then xs

else (x:(removeSmall xs))

removeSmall [0, 4,2,7,5,8, 5]

removeSmall [4,2,7,5,8, 5]

Page 29: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

29

removeSmall [4,2,7,5,8, 5, 1]

8. ssort [] = [] ; ssort list = small list : ssort (removeSmall list)

ssort [4,2,6,8,3,5]

Page 30: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

30

Exercise Set 4

type Length = Double

type Width = Double

type Height = Double

data Block = Block {lengthB :: Length, widthB :: Width, heightB :: Height}

deriving (Show)

block1 = Block 1.0 1.0 1.0

block2 = Block 2.0 3.0 4.0

block3 = Block 10.0 3.0 5.5

blocks = [block1, block2, block3]

volume (Block l w h) = l * w * h

volume2 b = lengthB b * widthB b * heightB b

averageVol list =

foldl (+) 0.0 (map volume list) / fromIntegral (length list)

volume1Block block =

Block ((lengthB block)/(volume block)**(1/3))

((widthB block)/(volume block)**(1/3))

((heightB block)/(volume block)**(1/3))

volume1s list = map volume1Block list

Exercise Set 5

import Text.Printf (printf)

type Name = String

type Price = Double

data Item = Item {nameItem :: Name, price :: Price} deriving (Show)

type CreditCardNumber = Int

data Customer =

Customer {nameCust :: Name, creditCardNumber :: CreditCardNumber}

deriving (Show)

data Purchase = Purchase {customer :: Customer, items :: [Item]}

deriving (Show)

item1 = Item "Shirt" 30.00

item2 = Item "Socks" 10.00

item3 = Item "Computer" 1000.00

item4 = Item "Meal" 17.50

item5 = Item "Mouse" 10.00

cust1 = Customer "Barry" 1111222233334444

cust2 = Customer "Jane" 2222333344445555

cust3 = Customer "Mary" 3333444455556666

Page 31: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

31

purchase1 = Purchase cust1 [item1, item2, item3]

purchase2 = Purchase cust2 [item4, item5]

purchase3 = Purchase cust3 [item3]

purchasesForToday = [purchase1, purchase2, purchase3]

total purchases = foldl (+) 0.00 $ map price (concat (map items purchases))

purchaseTotal purchase = foldl (+) 0.0 (map price (items purchase))

bigPurchases purchases =

filter (\purch -> purchaseTotal purch > 100.00) purchases

bigCustomers purchases = map customer $ bigPurchases purchases

namesOfBigCustomers purchases = map nameCust (bigCustomers purchases)

messagesToBigCustomers purchases =

map (\string -> "Dear " ++ string ++ ", You've spent more than $100.00.")

$ namesOfBigCustomers purchases

betterMessagesToBigCustomers purchases =

map (\purchase -> "Dear " ++ nameCust (customer purchase) ++

", You've purchased $" ++ show (purchaseTotal purchase) ++ " in goods.")

$ bigPurchases purchases

better2 purchases =

map mailing bigOnes

where bigOnes = bigPurchases purchases

mailing purchase =

salutation ++ firstName purchase ++ message purchase

salutation = "Dear "

firstName purchase =

nameCust (customer purchase)

message purchase = ", You've purchased $"

++ show (purchaseTotal purchase)

++ " in goods."

better3 purchases = let bigOnes = bigPurchases purchases

mailing purchase =

salutation ++ firstName purchase ++ message purchase

salutation = "Dear "

firstName purchase = nameCust (customer purchase)

message purchase =

", You've purchased $"

++ show (purchaseTotal purchase) ++ " in goods."

in map mailing bigOnes

Page 32: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

32

Exercise Set 6

adjIdent list = if length list == 0 || length list == 1

then False

else if head list == head (tail list) then True

else adjIdent (tail list)

Page 33: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

33

Exercise Set 7

module Conversions where

import Text.Printf

class Convertible a where

toUS :: a -> a

data Locale = US | France | England deriving (Show)

--data ValueWithUnits v u = ValueWithUnits { value :: v, units :: u }

-- deriving (Show)

data Currency = Currency { locale :: Locale, amount :: Double }

instance Show Currency where

show (Currency US x) = "$" ++ printf "%5.2f" x

show (Currency France x) = "E" ++ printf "%5.2f" x

show (Currency England x) = "L" ++ printf "%5.2f" x

instance Convertible Currency where

toUS (Currency US x) = Currency US x

toUS (Currency France x) = Currency US (x * 1.0638)

toUS (Currency England x) = Currency US (x * 1.25)

data Units = Feet | Meters

data Length = Length { len :: Double, units :: Units }

instance Show Length where

show (Length x Feet) = show x ++ "ft"

show (Length x Meters) = show x ++ "m"

instance Convertible Length where

toUS (Length x Feet) = Length x Feet

toUS (Length x Meters) = Length (x * 3.28) Feet

fromUS (Length x Feet) = Length (x * 0.3048) Meters

fromUS x = x

instance Eq Currency where

a == b = abs (amount (toUS a) - amount (toUS b)) < 0.01

instance Eq Length where

a == b = abs (len (toUS a) - len (toUS b)) < 0.01

population US = 322762018

population England = 55040000

population France = 738849002

data HowManyPeople = HowManyPeople { number :: Double, place :: Locale }

Page 34: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

34

instance Show HowManyPeople where

show (HowManyPeople d loc) = printf "%9.0f" d ++ " people in " ++ show loc

instance Convertible HowManyPeople where

toUS (HowManyPeople p US) = HowManyPeople p US

toUS (HowManyPeople p England) =

HowManyPeople (p * (population US) / (population England)) US

toUS (HowManyPeople p France) =

HowManyPeople (p * (population US) / (population France)) France

instance Eq HowManyPeople where

a == b = abs (number (toUS a) - number (toUS b)) < 100

data Scale = FAHR | CELS

instance Show Scale where

show FAHR = "Fahrenheit"

show CELS = "Celsius"

data Temp = Temp { value :: Double, scale :: Scale }

instance Show Temp where

show (Temp n s) = show n ++ " degrees " ++ show s

instance Convertible Temp where

toUS (Temp n FAHR) = Temp n FAHR

toUS (Temp n CELS) = Temp (n * 9/5 + 32) FAHR

instance Eq Temp where

temp1 == temp2 = abs (value (toUS temp1) - value (toUS temp2)) <= 0.01

main = do

putStrLn $ show $ Currency England 5

putStrLn $ show $ Currency US 10

putStrLn $ show $ Currency France 15

putStrLn $ show $ Currency England 1 == Currency US 1.25 --True

putStrLn $ show $ Currency England 1 == Currency France 1.17 --True

putStrLn $ show $ Currency England 1 == Currency US 1 --False

putStrLn $ show $ Length 7.5 Meters

putStrLn $ show $ Length 18 Feet

putStrLn $ show $ Length 1 Meters == Length 3.28 Feet --True

putStrLn $ show $ fromUS (Length 2 Feet)

putStrLn $ show $ fromUS (Length 2 Meters)

putStrLn $ show $ (Length 2 Feet) == (Length 0.609 Meters) --True

putStrLn $ show $ len (toUS (Length 2 Feet))

putStrLn $ show $ len (toUS (Length 0.609 Meters))

putStrLn $ show $

HowManyPeople 322762018 US == HowManyPeople 738849002 France --True

putStrLn $ show $

HowManyPeople 107587339 US == HowManyPeople 18346666 England --True

putStrLn $ show $ HowManyPeople 107587339 US

putStrLn $ show $ toUS (HowManyPeople 18346666 England)

Page 35: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

35

putStrLn $ show $ Temp 32 FAHR

putStrLn $ show $ toUS (Temp 100 CELS)

putStrLn $ show $ Temp 100 CELS == Temp 212 FAHR --True

putStrLn $ show $ Temp 0 CELS == Temp 32 FAHR --True

putStrLn $ show $ Temp 12 CELS == Temp 32 FAHR --False

Page 36: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

36

Exercise Set 8

waitingTime arrivalTime seatedTime = seatedTime - arrivalTime

maybeWaitingTime arrivalTime seatedTime =

if arrivalTime < seatedTime

then Just (seatedTime - arrivalTime)

else Nothing

maybeServiceWasOK waitingTime =

Just $ waitingTime >= 0 && waitingTime <= 30

question4 arrivalTime seatedTime =

maybeWaitingTime arrivalTime seatedTime >>= maybeServiceWasOK

maybeShowBoolean True = Just "Good Service"

maybeShowBoolean False = Just "Bad Service"

question6 arrivalTime seatedTime =

return seatedTime >>= maybeWaitingTime arrivalTime >>=

maybeServiceWasOK >>= maybeShowBoolean

main = do

print $ maybeWaitingTime 600 625

print $ maybeWaitingTime 625 600

print $ question4 600 625

print $ question4 600 635

print $ question4 700 600

print $ question6 600 625

print $ question6 600 635

print $ question6 700 600

Page 37: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

37

Exercise Set 9

type RoomNumber = Int

type Capacity = Int

type Occupancy = Int

data Room = Room {roomNumber::RoomNumber, capacity::Capacity,

occupancy::Occupancy}

instance Show Room where

show room = "Room " ++ show (roomNumber room) ++

": capacity " ++ show (capacity room) ++ " occupancy " ++

show (occupancy room)

makeRoomList :: [Room] -> IO [Room]

makeRoomList currentList = do

putStr "Enter a room number (or -1 to finish): "

roomNumberString <- getLine

let roomNum = read roomNumberString :: Int

if roomNum == -1 then do

printNicely currentList

return currentList

else do

putStr "What's that room's capacity? "

capString <- getLine

let cap = read capString :: Int

makeRoomList $

currentList ++ [Room roomNum cap 0]

printNicely list = mapM_ print list

fillRoom whichRoom numberOfPeople listOfRooms =

take whichRoom listOfRooms ++

[Room (roomNumber oldRoom) (capacity oldRoom) (numberOfPeople)] ++

drop (whichRoom + 1) listOfRooms

where oldRoom = listOfRooms!!whichRoom

findVacancies listOfRooms = do

if all (/= 0) (map occupancy listOfRooms)

then putStrLn "\nSorry! No vacancies...Exiting the program"

else do putStr $ "\nHow many people do you want to put into a room? "

howMany <- getLine

let numberOfPeople = read howMany :: Int

if numberOfPeople == 0 then putStrLn "Exiting the program"

else findVacancy

0 listOfRooms numberOfPeople

findVacancy whichRoom listOfRooms numberOfPeople = do

if whichRoom >= length listOfRooms

then do

putStrLn

"Sorry. We don't have sufficient capacity for your group."

findVacancies listOfRooms

Page 38: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

38

else if occupancy roomUnderConsideration /= 0 ||

numberOfPeople > capacity roomUnderConsideration

then findVacancy (whichRoom + 1) listOfRooms numberOfPeople

else do let newListOfRooms =

fillRoom whichRoom numberOfPeople listOfRooms

putStrLn "\n"

printNicely newListOfRooms

findVacancies newListOfRooms

where roomUnderConsideration = listOfRooms!!whichRoom

main = makeRoomList [] >>= findVacancies

Page 39: Haskell Programming Fundamentals: Learn Pure Functional ... · 9 More Haskell Expressions 500.0 * 2 500.0 * length [2,3] :t 2 500.00 * fromIntegral (length [2,3]) -- need type conversion

39

Exercise Set 10

import System.Random

prompt correctGuess = do

putStr "Guess a number from 1 to 10: "

currentGuessStr <- getLine

let currentGuess = read currentGuessStr :: Int

if currentGuess == correctGuess

then putStrLn "You win!"

else do

putStrLn "Incorrect..."

prompt correctGuess

main = do

g <- newStdGen

prompt $ fst (randomR (1::Int,10) g)