diff --git a/reveal/what-makes-haskell-unique.md b/reveal/what-makes-haskell-unique.md index e70cc66..07573d3 100644 --- a/reveal/what-makes-haskell-unique.md +++ b/reveal/what-makes-haskell-unique.md @@ -10,6 +10,15 @@ title: What Makes Haskell Unique
+ + --- ## Why uniqueness matters @@ -28,10 +37,22 @@ title: What Makes Haskell Unique * Higher order functions * Wait... is C functional? + + ---- __Haskell may be functional, but that doesn't make it unique__ + + ---- ## Let's Describe Haskell @@ -46,6 +67,14 @@ __Haskell may be functional, but that doesn't make it unique__ * Garbage collected * Immutability + + ---- __It's the combination of these features that makes Haskell unique__ @@ -111,7 +140,7 @@ useJsonBodies json1 json2 ---- - + ---- @@ -121,16 +150,20 @@ useJsonBodies json1 json2 * Need to fork threads, write to mutable variables, do some locking * Or be awesome -```haskell -(json1, json2) <- concurrently +
+ +
(json1, json2) <- concurrently
   (httpGet url1)
   (httpGet url2)
-useJsonBodies json1 json2
-```
+useJsonBodies json1 json2
-* Cheap green thread implementation -* Wonderful `async` library -* Builds on the async I/O system + + +
---- @@ -152,6 +185,10 @@ promise2.andThen(|json2| => useJsonBody(result.get()) ``` + + ---- ## Canceling in Haskell @@ -214,7 +251,7 @@ timeout tenSeconds expensiveComputation * Haskell differs in two ways: * Immutable by default, explicit kind of mutability * Mutating is an effect, tracked by the type system - + ---- ## Mutable Haskell @@ -243,6 +280,16 @@ let loop i = loop 1 ``` + + ---- ## Better Haskell @@ -282,6 +329,16 @@ func printScoreRange(results: Vector) { } ``` + + ---- ## Expected output @@ -292,6 +349,8 @@ Highest: 55 First result was by: Alice ``` + + ---- ## What's in printScoreRange? @@ -304,19 +363,33 @@ func printScoreRange(results: Vector) { } ``` +
+ Actual output: -``` -Lowest: 22 +
Lowest: 22
 Highest: 55
-First result was by: Charlie
-```
+First result was by: Charlie
Non-local changes broke our guessed result +
+ + ---- - + + + + ---- @@ -363,6 +436,8 @@ printScoreRange results = do * Concurrent data writes? Impossible! * We'll come back to mutable, multithreaded data + + ---- ## Mutability when needed @@ -374,6 +449,8 @@ printScoreRange results = do but they're __explicit__ 2. Temporary mutable copy, then freeze it + + ---- ## Freeze! @@ -432,6 +509,8 @@ runServer (|request| => { }) ``` +Looks reasonable, but... + ``` Thread 1: receive request: Alice gives $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 ``` + + ---- ## Locking @@ -466,6 +555,8 @@ Thread 2: try to lock Alice, but can't, so wait __Deadlock!__ +NOTE: Typical solution to this is to use locking, but it leads to other problems + ---- ## Software Transactional Memory @@ -486,6 +577,12 @@ runServer $ \request -> atomically $ do * `TVar` is an example of explicit mutation * 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 @@ -505,6 +602,12 @@ atomically $ do * Other side effects (like my bank account) are disallowed * 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 @@ -549,13 +652,15 @@ There are two problems with this code: 1. There's a major performance bug in it 2. It's much more cumbersome than it should be +NOTE: Kind of cheeky to hold off on laziness this long + ---- ## Space leaks 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 * Storing thunks is more expensive than simple types like `Int`s * Which values are evaluated in our `loop`? @@ -568,6 +673,13 @@ let loop i total = 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 @@ -582,9 +694,15 @@ let loop i !total = 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? +NOTE: + +* Can be explicit about what needs to be evaluated +* This is one approach, there are others +* Just added a `!` + ---- ## Looping (1) @@ -628,6 +746,8 @@ for(i := 1; i <= 1000000; i++) { * Getting harder to see the forest for the trees * 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 @@ -642,15 +762,19 @@ let loop i !total = in loop 1 0 ``` -But this is great +
-```haskell -sum [1..1000000] -``` +

But this is great

-* Doesn't it allocate 8mb of ints? -* Nope, laziness! -* Just a thunk telling us how to get the rest of the list +
sum [1..1000000]
+ +
    +
  • Doesn't it allocate 8mb of ints?
  • +
  • Nope, laziness!
  • +
  • Just a thunk telling us how to get the rest of the list
  • +
+ +
---- @@ -671,6 +795,13 @@ sum (map (`mod` 13) (filter even [1..1000000])) * Easy and natural to compose functions in a lazy context * 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 @@ -704,6 +835,11 @@ See also: `and`, `or`, `all`, `any` * Also, some inefficient functions still available, `foldl` vs `foldl'` +NOTE: + +* Generally partial functions are frowned upon +* But they're still in the language + ---- ## Summary of laziness @@ -751,10 +887,10 @@ See also: `and`, `or`, `all`, `any` ## Parser (and other) DSLs * Operator overloading! -* Abstractions `Alternative` a natural fit +* Abstractions like `Alternative` a natural fit * `parseXMLElement <|> parseXMLText`. * Able to reuse huge number of existing library functions, - e.g. `optional`, `many` + * `optional`, `many`, `foldMap` * General purpose `do`-notation is great ---- diff --git a/static/unique/deeper.jpg b/static/unique/deeper.jpg new file mode 100644 index 0000000..bfd3263 Binary files /dev/null and b/static/unique/deeper.jpg differ diff --git a/static/unique/doh.gif b/static/unique/doh.gif new file mode 100644 index 0000000..109c4c9 Binary files /dev/null and b/static/unique/doh.gif differ