2018-12-05 23:28

Page 1
- When a function is applied, it computes and returns a result, but nothing
else happens. The are no
*side effects*.- Good for modularity.
- Makes debugging and testing easier.
- The order in which things are computed does not affect the result.

- The focus is on the last point today.

- Purity means that the order in which things are computed does not matter.
- We get some freedom to choose how to compute things:
**Lazy evaluation**: delay computation until you know the result is needed.**Eager evaluation**: compute all subexpressions (one at a time)**Parallel evaluation**: compute many subexpressions in parallel

- Most languages are eager (also called
*strict*).

**Concurrency**is a programming style (paradigm) with- Many threads of control.
- Communication (message passing, shared memory) and synchronisation between threads.
- Need to think about about mutual exclusion, deadlocks and race conditions. Bugs can easily happen!

**Parallelism**is about making programs run faster on parallel hardware.- Can be supported with a simpler programming style.
- Needs no threads or explicit synchronisation/communication
- No deadlocks or race conditions.

- Can be supported with a simpler programming style.
- In most programming languages you need to use the support for concurrency even if you only want parallelism.

- Function arguments are
*not*computed*before*calling a function, but only*when/if*they are needed. - Parts of data structures are only computed when/if they are needed.
- You
*can*do parallel programming without worrying about the difficulties with concurrency- (Although it means fighting against laziness, in some sense...)

- Just a quick look. There is another course on Parallel Functional Programming.
- From

:**Control.Parallel**-- Spark a parallel computation

`par`**::**`a`**->**`b`**->**`b`

- Example:
-- f x = heavy1 x + heavy2 x

`f``x`**=**`par``y1`(`par``y2`(`y1``+``y2`))**where**`y1`**=**`heavy1``x``y2`**=**`heavy2``x`

- Using
`par`

*does not change the result*.- If the program is correct without
`par`

, it is still correct with`par`

!

- If the program is correct without

- Live demo: Fib.hs

- A parallel version of :
`map``pmap`**::**(`a`**->**`b`)**->**[`a`]**->**[`b`]`pmap``f`[]**=**[]`pmap``f`(`x`**:**`xs`)**=**`par``y`(`par``ys`(`y`**:**`ys`))**where**`y`**=**`f``x``ys`**=**`pmap``f``xs`

Only a

*small change in one place*. Results:Threads Solver A Solver B 1 14.0s 7.9s 2 7.3s 5.3s 4 4.2s 2.9s 8 4.0s 2.5s - Two different solvers: A finds all solutions, B finds one solution
- The table shows the time it takes to solve a particular puzzle (hard02.sud).
- Measured on my laptop, which has a 4-core processor with hyper-threading (hardware support for 8 parallel threads), GHC 8.2.2, -O2

- As a Haskell programmer, most of the time you probably don't need think about the difference. But:
- Sometimes laziness is very useful.
- It can allow you to make programs more
modular and elegant.
- John Hughes' early FP paper Why Functional Programming Matters illustrates this.

- It can even allow you to do things that at first seem impossible.

- It can allow you to make programs more
modular and elegant.
- Sometimes it can cause problems, e.g. space leaks.

- Function bodies are not computed until you call the function.
- When doing case analysis, only the selected branch is computed.
- The same for if-then-else.
- But consider this function
`choose``b``x``y`**=****if**`b`**then**`x`**else**`y`

- What happens if I use everywhere instead of if-then-else?
`choose`- When does an equation like the one above mean that we have
two
*equal*,*interchangeable*things?

- When does an equation like the one above mean that we have
two

- Live demo: Laziness.hs

`:help`

Commands available from the prompt: ... :print [<name> ...] show a value without forcing its computation :sprint [<name> ...] simplified version of :print ...

`ones`**=**1**:**`ones``numbers`**=**[1**..**] -- count n = [n..]`countUp``n`**=**`n`**:**`countUp`(`n``+`1) -- fromTo start stop == [start .. stop]`fromTo``start``stop`**=**`take`(`stop``-``start``+`1) (`countUp``start`) -- Fibonacci numbers`fibs`**=**1**:**1**:**`zipWith`(`+`)`fibs`(`tail``fibs`)- In eager languages, only function definitions can be recursive…

- We can define the list of
*all*prime numbers (using the Sieve of Eratosthenes)… `primes`**::**[**Integer**]`primes`**=**`sieve`[2**..**]**where**`sieve`(`p`**:**`xs`)**=**`p`**:**`sieve`[`x`**|**`x`**<-**`xs`,`x```mod```p``/=`0]- ... and
*separately*decide how many of them we want to use. -- | The twenty first primes

`ex1`**=**`take`20`primes`-- | All primes below 100`ex2`**=**`takeWhile`(`<`100)`primes`- Laziness helps with modularity!
- How would you do these examples in an eager language?

`repeat`**::**`a`**->**[`a`]`cycle`**::**[`a`]**->**[`a`]`iterate`**::**(`a`**->**`a`)**->**`a`**->**[`a`]`enumFrom`**::****Enum**`a`**=>**`a`**->**[`a`] -- depending on the type`repeat`1`==`[1,1,1,1,`…`]`cycle`[1,2,3]`==`[1,2,3,1,2,3,1,2,3,1,2,3,`…`]`iterate`(`*`2) 1`==`[1,2,4,8,16,32,64,128,256,512,`…`][0

**..**]`==``enumFrom`0`==`[0,1,2,3,4,5,6,7,8,9,`…`]

`repeat`

generates an infinite list but we can use it even if we only want a finite list.`replicate`**::****Int****->**`a`**->**[`a`]`replicate``n``x`**=**`take``n`(`repeat``x`)`filter`

returns*all*elements of a list that pass a test, but we can use it even if we only want*the first*one.`find`**::**(`a`**->****Bool**)**->**[`a`]**->****Maybe**`a``find``p``xs`**=**`listToMaybe`(`filter``p``xs`)- The easiest way to number the elements in a list is to zip with an infinite list of numbers. Example:
`findIndices`**::**(`a`**->****Bool**)**->**[`a`]**->**[**Int**]`findIndices``p``xs`**=**[`i`**|**(`i`,`x`)**<-**`zip`[0**..**]`xs`,`p``x`]

- Using Newton's method to compute the square root of a number
`sqroot``x`**=**`head`[`a2`**|**(`a1`,`a2`)**<-**`zip``as`(`tail``as`),`abs`(`a1``-``a2`)`<`1`e``-`10]**where**`as`**=**`iterate``next`1 -- infinite list of improving approx.`next``a`**=**(`a``+``x``/``a`)`/`2 -- the next approx.

- The standard function is defined like this:
`zip``zip`(`x`**:**`xs`) (`y`**:**`ys`)**=**(`x`,`y`)**:**`zip``xs``ys``zip`**_****_****=**[]

- Does this property hold for all lists and
`xs`?`ys``prop_zip_length``xs``ys`**=**`length`(`zip``xs``ys`)`==``min`(`length``xs`) (`length``ys`)

- Consider
`zip [1..] "Haskell"`

[(1,'H'),(2,'a'),(3,'s'),(4,'k'),(5,'e'),(6,'l'),(7,'l')]

- We get a finite result even when one of the lists is infinite.
`length [1..]`

- We get no result from for infinte lists.
`length` - The property fails. Do we blame laziness? Can we fix it?

- The function is too strict.
`length` - It doesn't return the length until it reaches the end of the list.
- The strictness comes from the use of and the
(

`+`)type...**Int** `length`[]**=**0`length`(`x`**:**`xs`)**=**1`+``length``xs`

- Peano:
- a natural number is either zero, or the successor of a natural number
- ℕ = {0} ∪ {S
`n`|`n`∈ ℕ}

- We can express this directly as a data type in Haskell
**data****Nat****=****Z****|****S****Nat****deriving**(**Eq**,**Ord**,**Show**)

- Now we can do
`infinity`**=****S**`infinity`

`length`**::**[`a`]**->****Nat**`length`[]**=****Z**`length`(`x`**:**`xs`)**=****S**(`length``xs`)- With laziness, this allows the length to be computed
*gradually*. - Infinite lists have infinite length.
- We can test things like , even though
`length``primes``>`1000is infinite!`primes`

`prop_zip_length [1..] "Haskell"`

True- Note: normal testing with QuickCheck wouldn't have caught this problem,
since doesn't generate infinite lists.
`arbitrary`**::****Gen**[`a`] - While this property is true even for infinite lists, there are
other properties that are only true for finite lists:
`prop_reverse_reverse``xs`**=**`reverse`(`reverse``xs`)`==``xs`

- Define a function
`search`**::**(**Nat****->****Bool**)**->****Maybe****Nat**- such that returns
`search``p`- where
**Just**`n`is the smallest number such that`n``p``n``==`**True** - , if
**Nothing**for all`p``n``==`**False**`n`

- Examples
`search (\ n -> n*n==25)`

Just (S (S (S (S (S Z)))))`search (\ n -> n*n==26)`

Nothing

**data****Tree**`a`**=****Leaf**`a`**|****Branch**(**Tree**`a`) (**Tree**`a`)`eqTree`**::****Eq**`a`**=>****Tree**`a`**->****Tree**`a`**->****Bool**`eqTree`(**Leaf**`a1`) (**Leaf**`a2`)**=**`a1``==``a2``eqTree`(**Branch**`l1``r1`) (**Branch**`l2``r2`)**=**`eqTree``l1``l2``&&``eqTree``r1``r2``eqTree`**_****_****=****False**- Simultaneous recursion on two trees
- Stops and returns as soon as a difference is encountered.
**False** - Works the same in both strict and lazy languages (assuming is lazy).
(

`&&`)

- Implementent the function
`eqFringe`**::****Eq**`a`**=>****Tree**`a`**->****Tree**`a`**->****Bool**

- that checks two trees have the same sequence of leaves.
- That is, the trees don't need to have the same shape.

- Stop and return as soon as you find a difference.
**False**

- Since reads the contents of the file lazily, programs that process the contents sequentially run in constant space.
`readFile` `countLines`**::****FilePath****->****IO****Int**`countLines``filename`**=****do**`s`**<-**`readFile``filename``return`(`length`(`lines``s`))- But reading a file and writing data back to the same file can fail for the same reason.
`sortFile`**::****FilePath****->****IO**()`sortFile``filename`**=****do**`input`**<-**`readFile``filename``writeFile``filename`(`sortLines``input`)**where**`sortLines`**=**`unlines``.``sort``.``lines`- Exercise: rewrite so that it works.
`sortFile`

- Computerphile: Infinite Data Structures: To Infinity & Beyond! (16 minute video)
- Graham Hutton shows some things you can do with infinite lists in Haskell, including the Sieve of Eratosthenes

- Quora: How is lazy evaluation implemented in functional programming languages?