Update with speaker notes
This commit is contained in:
parent
e84294ee71
commit
4ab402949b
3 changed files with 161 additions and 25 deletions
|
@ -10,6 +10,15 @@ title: What Makes Haskell Unique
|
||||||
|
|
||||||
<div><img src="/static/fpcomplete-logo.png" style="border:0;margin:0"></div>
|
<div><img src="/static/fpcomplete-logo.png" style="border:0;margin:0"></div>
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Good morning, welcome</li>
|
||||||
|
<li>FP Complete helps people adopt Haskell</li>
|
||||||
|
<li>Need to know: <b>What makes Haskell unique</b></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Why uniqueness matters
|
## Why uniqueness matters
|
||||||
|
@ -28,10 +37,22 @@ title: What Makes Haskell Unique
|
||||||
* Higher order functions
|
* Higher order functions
|
||||||
* Wait... is C functional?
|
* Wait... is C functional?
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Haskell is functional</li>
|
||||||
|
<li>So are lots of others</li>
|
||||||
|
<li>Even if you include closures, still many choices</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
__Haskell may be functional, but that doesn't make it unique__
|
__Haskell may be functional, but that doesn't make it unique__
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
Lots of things could describe Haskell
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Let's Describe Haskell
|
## Let's Describe Haskell
|
||||||
|
@ -46,6 +67,14 @@ __Haskell may be functional, but that doesn't make it unique__
|
||||||
* Garbage collected
|
* Garbage collected
|
||||||
* Immutability
|
* Immutability
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Some features are rare: pure and lazy</li>
|
||||||
|
<li>Some are common</li>
|
||||||
|
<li>No one feature is enough to motivate using Haskell</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
__It's the combination of these features that makes Haskell unique__
|
__It's the combination of these features that makes Haskell unique__
|
||||||
|
@ -111,7 +140,7 @@ useJsonBodies json1 json2
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
<img src="http://i1.kym-cdn.com/photos/images/newsfeed/000/531/557/a88.jpg">
|
<img src="/static/unique/deeper.jpg" style="width:100%">
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -121,16 +150,20 @@ useJsonBodies json1 json2
|
||||||
* Need to fork threads, write to mutable variables, do some locking
|
* Need to fork threads, write to mutable variables, do some locking
|
||||||
* Or be awesome
|
* Or be awesome
|
||||||
|
|
||||||
```haskell
|
<div class="fragment">
|
||||||
(json1, json2) <- concurrently
|
|
||||||
|
<pre><code class="haskell">(json1, json2) <- concurrently
|
||||||
(httpGet url1)
|
(httpGet url1)
|
||||||
(httpGet url2)
|
(httpGet url2)
|
||||||
useJsonBodies json1 json2
|
useJsonBodies json1 json2</code></pre>
|
||||||
```
|
|
||||||
|
|
||||||
* Cheap green thread implementation
|
<ul>
|
||||||
* Wonderful `async` library
|
<li>Cheap green thread implementation</li>
|
||||||
* Builds on the async I/O system
|
<li>Wonderful `async` library</li>
|
||||||
|
<li>Builds on the async I/O system</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -152,6 +185,10 @@ promise2.andThen(|json2| =>
|
||||||
useJsonBody(result.get())
|
useJsonBody(result.get())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
So far: elegant in Haskell, but not terribly difficult in other languages.
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Canceling in Haskell
|
## Canceling in Haskell
|
||||||
|
@ -243,6 +280,16 @@ let loop i =
|
||||||
loop 1
|
loop 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>In pure code, we cannot create, read, or modify a mutable variable</li>
|
||||||
|
<li>Have to use non-pure code</li>
|
||||||
|
<li>Lots of ceremony for something simple, so don't do that</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Better Haskell
|
## Better Haskell
|
||||||
|
@ -282,6 +329,16 @@ func printScoreRange(results: Vector<TestResult>) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Personally think the phrase "reasoning about code" is overused, but here's a concrete example.</li>
|
||||||
|
<li><i>Describe slide</i></li>
|
||||||
|
<li>What's the expected output? Reasonable to guess...</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Expected output
|
## Expected output
|
||||||
|
@ -292,6 +349,8 @@ Highest: 55
|
||||||
First result was by: Alice
|
First result was by: Alice
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<aside class="notes">But let's see the definition of <code>printScoreRange</code></aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## What's in printScoreRange?
|
## What's in printScoreRange?
|
||||||
|
@ -304,19 +363,33 @@ func printScoreRange(results: Vector<TestResult>) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<div class="fragment">
|
||||||
|
|
||||||
Actual output:
|
Actual output:
|
||||||
|
|
||||||
```
|
<pre>Lowest: 22
|
||||||
Lowest: 22
|
|
||||||
Highest: 55
|
Highest: 55
|
||||||
First result was by: Charlie
|
First result was by: Charlie</pre>
|
||||||
```
|
|
||||||
|
|
||||||
Non-local changes broke our guessed result
|
Non-local changes broke our guessed result
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="notes">Our assumptions changed because of mutation</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
<img src="http://www.raminajmi.dk/wp-content/uploads/2014/06/homer-simpson-doh.gif">
|
<img src="/static/unique/doh.gif">
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>results</code> from <code>main</code> has been modified</li>
|
||||||
|
<li>Can't just look at <code>main/code> to understand what will happen</li>
|
||||||
|
<li>Need to be aware of mutation happening in the rest of the program</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -363,6 +436,8 @@ printScoreRange results = do
|
||||||
* Concurrent data writes? Impossible!
|
* Concurrent data writes? Impossible!
|
||||||
* We'll come back to mutable, multithreaded data
|
* We'll come back to mutable, multithreaded data
|
||||||
|
|
||||||
|
<aside class="notes">Multithreaded cases is more interesting. We can easily parallelize our previous code.</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Mutability when needed
|
## Mutability when needed
|
||||||
|
@ -374,6 +449,8 @@ printScoreRange results = do
|
||||||
but they're __explicit__
|
but they're __explicit__
|
||||||
2. Temporary mutable copy, then freeze it
|
2. Temporary mutable copy, then freeze it
|
||||||
|
|
||||||
|
<aside class="notes">Option 1 breaks our guarantees. But still better than other languages: know exactly which data to look at</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Freeze!
|
## Freeze!
|
||||||
|
@ -432,6 +509,8 @@ runServer (|request| => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Looks reasonable, but...
|
||||||
|
|
||||||
```
|
```
|
||||||
Thread 1: receive request: Alice gives $25
|
Thread 1: receive request: Alice gives $25
|
||||||
Thread 2: receive request: Alice receives $25
|
Thread 2: receive request: Alice receives $25
|
||||||
|
@ -441,6 +520,16 @@ Thread 1: set Alice's account to $25
|
||||||
Thread 2: set Alice's account to $75
|
Thread 2: set Alice's account to $75
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>What if you actually need to mutate values, and from multiple threads?</li>
|
||||||
|
<li><i>Describe slide</i></li>
|
||||||
|
<li>Alice ends up with either $25 or $75 instead of $50</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Locking
|
## Locking
|
||||||
|
@ -466,6 +555,8 @@ Thread 2: try to lock Alice, but can't, so wait
|
||||||
|
|
||||||
__Deadlock!__
|
__Deadlock!__
|
||||||
|
|
||||||
|
NOTE: Typical solution to this is to use locking, but it leads to other problems
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Software Transactional Memory
|
## Software Transactional Memory
|
||||||
|
@ -486,6 +577,12 @@ runServer $ \request -> atomically $ do
|
||||||
* `TVar` is an example of explicit mutation
|
* `TVar` is an example of explicit mutation
|
||||||
* Alternatives: `IORef`, `MVar`
|
* Alternatives: `IORef`, `MVar`
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
* There are helper functions to make this shorter
|
||||||
|
* Want to make a point with the longer code
|
||||||
|
* STM will automatically retry when needed
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## The role of purity
|
## The role of purity
|
||||||
|
@ -505,6 +602,12 @@ atomically $ do
|
||||||
* Other side effects (like my bank account) are disallowed
|
* Other side effects (like my bank account) are disallowed
|
||||||
* Safe for runtime to retry thanks to purity
|
* Safe for runtime to retry thanks to purity
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
* `buyBitcoins` needs to go to an exchange and spend $100,000
|
||||||
|
* Due to retry, this code could spend $10m
|
||||||
|
* This is where purity steps in
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Summary of STM
|
## Summary of STM
|
||||||
|
@ -549,13 +652,15 @@ There are two problems with this code:
|
||||||
1. There's a major performance bug in it
|
1. There's a major performance bug in it
|
||||||
2. It's much more cumbersome than it should be
|
2. It's much more cumbersome than it should be
|
||||||
|
|
||||||
|
NOTE: Kind of cheeky to hold off on laziness this long
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Space leaks
|
## Space leaks
|
||||||
|
|
||||||
Consider `let foo = 1 + 2`
|
Consider `let foo = 1 + 2`
|
||||||
|
|
||||||
* `foo` isn't `3`, it's an instruction on how to create `3`
|
* `foo` isn't `3`, it's an instruction for how to create `3`
|
||||||
* `foo` is a _thunk_ until it's evaluated
|
* `foo` is a _thunk_ until it's evaluated
|
||||||
* Storing thunks is more expensive than simple types like `Int`s
|
* Storing thunks is more expensive than simple types like `Int`s
|
||||||
* Which values are evaluated in our `loop`?
|
* Which values are evaluated in our `loop`?
|
||||||
|
@ -568,6 +673,13 @@ let loop i total =
|
||||||
in loop 1 0
|
in loop 1 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
* The bane of laziness is space leaks, which you may have hard
|
||||||
|
about. Need to understand how laziness is implemented.
|
||||||
|
* Explain why `i` is forced and `total` is not
|
||||||
|
* Builds a tree, lots of CPU and memory pressure
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Explicit strictness
|
## Explicit strictness
|
||||||
|
@ -582,9 +694,15 @@ let loop i !total =
|
||||||
in loop 1 0
|
in loop 1 0
|
||||||
```
|
```
|
||||||
|
|
||||||
* Needing to do this is a downside of Haskell
|
* Needing to do this is a downside of Haskell's laziness
|
||||||
* But do we get any benefits in return?
|
* But do we get any benefits in return?
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
* Can be explicit about what needs to be evaluated
|
||||||
|
* This is one approach, there are others
|
||||||
|
* Just added a `!`
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Looping (1)
|
## Looping (1)
|
||||||
|
@ -628,6 +746,8 @@ for(i := 1; i <= 1000000; i++) {
|
||||||
* Getting harder to see the forest for the trees
|
* Getting harder to see the forest for the trees
|
||||||
* If our logic was more complicated, code reuse would be an issue
|
* If our logic was more complicated, code reuse would be an issue
|
||||||
|
|
||||||
|
NOTE: Example of more complicated use case, writing a lookahead parser
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Some better Haskell
|
## Some better Haskell
|
||||||
|
@ -642,15 +762,19 @@ let loop i !total =
|
||||||
in loop 1 0
|
in loop 1 0
|
||||||
```
|
```
|
||||||
|
|
||||||
But this is great
|
<div class="fragment">
|
||||||
|
|
||||||
```haskell
|
<p>But this is great</p>
|
||||||
sum [1..1000000]
|
|
||||||
```
|
|
||||||
|
|
||||||
* Doesn't it allocate 8mb of ints?
|
<pre><code class="haskell">sum [1..1000000]</code></pre>
|
||||||
* Nope, laziness!
|
|
||||||
* Just a thunk telling us how to get the rest of the list
|
<ul>
|
||||||
|
<li>Doesn't it allocate 8mb of ints?</li>
|
||||||
|
<li>Nope, laziness!</li>
|
||||||
|
<li>Just a thunk telling us how to get the rest of the list</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -671,6 +795,13 @@ sum (map (`mod` 13) (filter even [1..1000000]))
|
||||||
* Easy and natural to compose functions in a lazy context
|
* Easy and natural to compose functions in a lazy context
|
||||||
* Avoids doing unnecessary work or using too much memory
|
* Avoids doing unnecessary work or using too much memory
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
* Never using more than a few machine words
|
||||||
|
* Other GHC optimizations avoid allocating any thunks
|
||||||
|
* Not covering that today
|
||||||
|
* Mixed bag, but functional+lazy=declarative, performant
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Short circuiting for free
|
## Short circuiting for free
|
||||||
|
@ -704,6 +835,11 @@ See also: `and`, `or`, `all`, `any`
|
||||||
* Also, some inefficient functions still available, `foldl` vs
|
* Also, some inefficient functions still available, `foldl` vs
|
||||||
`foldl'`
|
`foldl'`
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
* Generally partial functions are frowned upon
|
||||||
|
* But they're still in the language
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Summary of laziness
|
## Summary of laziness
|
||||||
|
@ -751,10 +887,10 @@ See also: `and`, `or`, `all`, `any`
|
||||||
## Parser (and other) DSLs
|
## Parser (and other) DSLs
|
||||||
|
|
||||||
* Operator overloading!
|
* Operator overloading!
|
||||||
* Abstractions `Alternative` a natural fit
|
* Abstractions like `Alternative` a natural fit
|
||||||
* `parseXMLElement <|> parseXMLText`.
|
* `parseXMLElement <|> parseXMLText`.
|
||||||
* Able to reuse huge number of existing library functions,
|
* Able to reuse huge number of existing library functions,
|
||||||
e.g. `optional`, `many`
|
* `optional`, `many`, `foldMap`
|
||||||
* General purpose `do`-notation is great
|
* General purpose `do`-notation is great
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
BIN
static/unique/deeper.jpg
Normal file
BIN
static/unique/deeper.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
static/unique/doh.gif
Normal file
BIN
static/unique/doh.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Loading…
Reference in a new issue