Browse Source

Initial Applied Haskell content, not complete

applied-haskell
Michael Snoyman 5 years ago
parent
commit
7995cf5a55
No known key found for this signature in database GPG Key ID: A048E8C057E86876
  1. 55
      reveal/applied-haskell/applied-haskell.md
  2. 28
      reveal/applied-haskell/containers.md
  3. 147
      reveal/applied-haskell/overview.md
  4. 364
      reveal/applied-haskell/string-types.md
  5. 108
      reveal/applied-haskell/vector.md
  6. 24
      static/applied-haskell.html

55
reveal/applied-haskell/applied-haskell.md

@ -0,0 +1,55 @@
* FIXME split into separate files
---
* Strictness annotations
* `data Foo = Foo !Int` vs `newtype Foo = Foo Int`
* Unpack
* Primitive types
* Primitive `IO`
* Foldable, MonoFoldable
* classy-prelude
* Monad transformers, MonadIO
* Logging: trace vs monad-logger
* Exceptions
* Mutable variables
* IORef
* MVar
* TVar, and STM in general
* async
* Write a concurrent program that prints the numbers 1 to 100 in
one thread, pausing one second between number, and printing
"Hello World" every two seconds in another thread
* typed-process
* hspec
* Profiling
* Bad average
* Analyzing GHC core
* conduit
* Good average
* When to use conduit
* Take a file and, for each line, print out the number of bytes in
the line (try using bytestring directly and then conduit)
* http://stackoverflow.com/questions/42675764/read-large-lines-in-huge-file-without-buffering/42676477#42676477
* http-conduit
* Make an HTTP request and print the returned HTML to the console
* aeson and yaml
* Web services: WAI vs servant vs Yesod
* FFI
* Unsafe bytestring operations
* Efficient file copy

28
reveal/applied-haskell/containers.md

@ -0,0 +1,28 @@
# containers
* FIXME
---
# Maps
* Dictionaries
* __Map__ping from keys to values
* See what I did there?
* Ordered, hashed, and ints
---
# Sets
* `Map`s without values
* Ordered, hashed, and ints
---
# The rest of containers
* Not covering it here
* `Data.Sequence` (efficient append/prepend, very useful!)
* `Data.Graph`
* `Data.Tree`

147
reveal/applied-haskell/overview.md

@ -0,0 +1,147 @@
# Applied Haskell
* Michael Snoyman
* LambdaConf 2017
---
## Overview
* Haskell is a "revolutionary" language
* We don't care about that today very much
* How do I get things done in Haskell?
* Focus:
* Familiarity with best-in-class libraries
* Get better at picking up Haskell idioms
* Learn tooling a bit more
* Pick up runtime system idiosyncracies
----
## What we won't do
* Focus on advanced features of the language
* If they come up, we'll cover them, but they're not the focus
* Learn math or category theory
* We just want to do enough to be proficient with common libraries
* Teach basic Haskell syntax
* Assuming such basic familiarity as a prereq
----
## Learning Resources
* https://www.fpcomplete.com/haskell-syllabus
* https://haskell-lang.org/documentation
* https://haskell-lang.org/libraries
* https://www.stackage.org/ (and its Hoogle search)
* http://www.yesodweb.com/book
* https://www.schoolofhaskell.com
---
## Tooling overview
* __GHC__ Compiler
* GHCJS, JHC, others...
* Almost everyone uses GHC
* __Stack__ build tool
* Downloads GHC for you
* Installs packages
* __Emacs__ editor
* intero-mode works pretty well
* And many others
* __hlint__ linter, __hoogle__ search
----
## Cabal packages
* Have a name and version number
* 0-1 libraries
* 0 or more executables
* 0 or more test suites
* 0 or more benchmarks
* Configured via `projectname.cabal`
* Or if you're cool: `package.yaml` and `hpack`
* Can be released as open source to Hackage
----
## hpack example
* FIXME
----
## Stackage snapshot
* Specifies a specific GHC version
* Specifies a collection of Cabal packages from Hackage
* Specifies build flags
* Nightly: one per day, try to take the latest version from Hackage
* LTS (Long Term Support): weekly, tries to maintain backwards
compatibility based on PVP
* Guarantee: we got these packages to compile together and pass tests
----
## Stack projects
* Configured via `stack.yaml` file
* 0 or more local Cabal packages
* Specifies a *resolver*
* Stackage snapshot (`nightly-2017-01-01`, `lts-8.9`)
* GHC version (no extra packages)
* Can add extra deps, flag overrides, much more
* Can pull in packages from a Git repo
----
## stack.yaml example
* FIXME
----
## Quiz: package or stack.yaml
* FIXME
----
## Stack script interpreter
* Useful for single-file code
* Nicely portable, just needs Stack
* We'll use it a lot today
----
## Stack script example
* FIXME
----
## GHCi
* REPL
* Access with `stack ghci` or `stack exec ghci`
* I'm personally GHCi-challenged, we won't use it much (if at all)
here
---
## Generic data structures
* Lists
* `Vector`s (boxed, unboxed, and storable)
* `Map`, `HashMap`, and `IntMap`
* `Set`, `HashSet`, and `IntSet`
----
## Quick: Pick the data structure
* FIXME

364
reveal/applied-haskell/string-types.md

@ -0,0 +1,364 @@
## String types
* Strings (== list of `Char`s)
* `ByteString` (strict and lazy)
* `Text` (strict and lazy)
----
## Quiz: Pick the String type
* Complete works of Shakespeare
* PNG-formatted image
* HTTP headers
* Contents of an HTML file
* Contents of a JSON file
----
## Builders
* `ByteString` builder
* `Text` builder
<img src="https://i.imgflip.com/1nuiyg.jpg" height="400px">
----
## APIs
* Mostly conforming APIs
* Designed to be imported qualified
* We'll see some typeclasses later to generalize
* Some types are *monomorphic* or *constrained*
* `Text` and `ByteString` are monomorphic
* Unboxed and storable have constraints on values
* `Map`, `HashMap`, `Set`, `HashSet` have constraints on keys
---
# bytestring
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import Data.Monoid ((<>))
main :: IO ()
main = do
let fp = "somefile.txt"
B.writeFile fp $ "Hello " <> "World!"
contents <- B.readFile fp
B.putStrLn $ B.takeWhile (/= 32) contents
```
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import Data.Monoid ((<>))
import Data.Word8 (_space)
main :: IO ()
main = do
let fp = "somefile.txt"
B.writeFile fp $ "Hello " <> "World!"
contents <- B.readFile fp
B.putStrLn $ B.takeWhile (/= _space) contents
```
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import Data.Monoid ((<>))
main :: IO ()
main = do
let fp = "somefile.txt"
B.writeFile fp $ "Hello " <> "World!"
contents <- B.readFile fp
B8.putStrLn $ B8.takeWhile (/= ' ') contents
```
----
## Questions
* Is our code exception safe?
* Is there any laziness in the code?
* Discuss: to `Char8` or not `Char8` for `Handle` I/O
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
fibs :: [Int]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fibsBS :: [B.ByteString]
fibsBS = map (B8.pack . show) fibs
main :: IO ()
main = B8.putStr $ B8.unlines $ take 5 fibsBS
```
* Propose alternative implementations
* Discuss: performance and assumptions
* `putStr` vs `putStrLn`
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.ByteString.Builder as BB
import System.IO (stdout)
import Data.Monoid ((<>))
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
main = BB.hPutBuilder stdout $ foldr
(\i rest -> BB.intDec i <> "\n" <> rest)
mempty
(take 5 fibs)
```
* What does the performance of this look like?
* `foldr` or `foldl`?
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B8
main :: IO ()
main = do
let bs = "Non Latin characters: שלום"
B8.putStrLn bs
print bs
```
```
Non Latin characters: ????
"Non Latin characters: \233\220\213\221"
```
* Implicit data loss!
* Terminal doesn't like incorrect character encodings
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BL8
import Control.Spoon
main :: IO ()
main = do
let bomb = ['h', 'e', 'l', 'l', 'o', undefined]
print $ spoon $ take 5 bomb
print $ spoon $ B.take 5 $ B8.pack bomb
print $ spoon $ BL.take 5 $ BL8.pack bomb
```
<pre class="fragment"><code>Just "hello"
Nothing
Nothing</code></pre>
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BL8
import Control.Spoon
main :: IO ()
main = do
let bomb = concat $ replicate 10000 "hello" ++ [undefined]
print $ spoon $ take 5 bomb
print $ spoon $ B.take 5 $ B8.pack bomb
print $ spoon $ BL.take 5 $ BL8.pack bomb
```
<div class="fragment"><pre><code>Just "hello"
Nothing
Just "hello"</code></pre><p>Arbitrary chunking size, beware!</p></div>
----
## Quiz: bytestring
* FIXME
----
## Exercise: file copy
* Copy `source.txt` to `dest.txt`
* Step 1: make it work
* Step 2: make it work for large files
* Hint: you'll want `withBinaryFile` and `hGetSome`
* Use Stackage to search for them
----
## Solution 1
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
import qualified Data.ByteString as B
main = B.readFile "source.txt" >>= B.writeFile "dest.txt"
```
* Problem: reads entire file into memory
----
## Solution 2
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
import qualified Data.ByteString as B
import System.IO
import Data.Function (fix)
import Control.Monad (unless)
main =
withBinaryFile "source.txt" ReadMode $ \hIn ->
withBinaryFile "dest.txt" ReadMode $ \hOut ->
fix $ \loop -> do
bs <- B.hGetSome hIn 4096
unless (B.null bs) $ do
B.hPut hOut bs
loop
```
* Problem: allocates lots of buffers
* We'll look at the FFI later about this
---
## text
(Kinda just like bytestring)
----
* FIXME: copy lots of examples from above
----
```haskell
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Data.Text.Encoding as TE
main = do
let text = "This is some text, with non-Latin chars: שלום"
bs = TE.encodeUtf8 text
B.writeFile "content.txt" bs
bs2 <- B.readFile "content.txt"
let text2 = TE.decodeUtf8 bs2
TIO.putStrLn text2
```
* What character encoding did `TIO.putStrLn` use?
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Data.Text.Encoding as TE
main = do
let text = "This is some text, with non-Latin chars: שלום"
bs = TE.encodeUtf8 text
B.writeFile "content.txt" bs
text2 <- TIO.readFile "content.txt"
TIO.putStrLn text2
```
* Good luck!
* http://www.snoyman.com/blog/2016/12/beware-of-readfile
----
## The char encoding debacle
* File I/O: use bytestring
* Standard handles
* Interacting with user: use text
* Interacting with pipe: use bytestring
* Also: text I/O is slow, you can use the `say` package
----
## Exercise
Take a UTF-8 encoded file and generate a UTF-16 encoded file
----
```haskell
#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.Text.Encoding as TE
main :: IO ()
main = do
bsUtf8 <- B.readFile "utf8.txt"
let text = TE.decodeUtf8 bsUtf8
bsUtf16 = TE.encodeUtf16LE text
B.writeFile "utf16.txt" bsUtf16
```
----
## Laziness
* Don't read files lazily
* Writing lazy data isn't actually lazy I/O
* We'll get to streaming data/conduit later

108
reveal/applied-haskell/vector.md

@ -0,0 +1,108 @@
# vector
* Just like lists, but not
* Packed representation
* Spine strict (sometimes value strict)
----
## The Three Types
* __Boxed__ Array of pointers
* `Data.Vector`
* __Storable__ Binary rep, *pinned*
* `Data.Vector.Storable`
* __Unboxed__ Binary rep, *unpinned*
* `Data.Vector.Unboxed`
* Also has a generic interface unifying the others (more on that later)
* Ignore `Primitive`, use `Unboxed`
----
## Pinned vs unpinned
* Pinned
* `malloc`/`free`
* GC cannot move it around
* Safe to pass to FFI
* Can lead to memory fragmentation
* Unpinned
* Fully managed by GC/RTS
* Can be compacted by GC
* Cannot be passed directly to FFI
----
## Compared to lists
* Less pointer indirection
* Unboxed and storable: no pointer indirection
* Less memory used for holding pointers
* Consing is more expensive
* No laziness/infinite data structures
* Advice: most cases, vectors are what you want
----
## Mutable vs immutable
* Both mutable and immutable interfaces
* Mutable actions from inside `IO` or `ST`
* Freeze a mutable to an immutable
* Thaw an immutable to a mutable
* Immutable API: similar to lists
* Mutable API: similar to C arrays
----
## What do I use?
* Unless you know otherwise: immutable
* If unboxed is possible, use it
* Otherwise, if storable is possible, use it
* Otherwise, use boxed
* Generic algorithm? Use `Generic`
* Polymorphic container? Stick with boxed
----
## Stream fusion
* Avoid intermediate data structures
* Leverages stream representation and rewrite rules
* Only applies to immutable API
* When it works, gives great speedups
---
## Vector basics
* FIXME examples
----
## Unboxed
* FIXME example
----
## Generic API
* FIXME example
----
* FIXME examples of putting undefined into vectors
---
## Mutable vectors
* FIXME
----
## vector-algorithms
* FIXME

24
static/applied-haskell.html

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Applied Haskell</title>
</head>
<body>
<h1>Applied Haskell</h1>
<ul>
<li>
<a href="/reveal/applied-haskell/overview">Overview</a>
</li>
<li>
<a href="/reveal/applied-haskell/string-types">String Types</a>
</li>
<li>
<a href="/reveal/applied-haskell/vector">Vector</a>
</li>
<li>
<a href="/reveal/applied-haskell/containers">Containers</a>
</li>
</ul>
</body>
</html>
Loading…
Cancel
Save