Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 5b1684d26a
Fetching contributors…

Cannot retrieve contributors at this time

1214 lines (1151 sloc) 40.829 kB
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head>
<title>Themenabend Haskell &amp; Yesod</title>
<!-- Slidy: http://www.w3.org/Talks/Tools/Slidy2/ -->
<link rel="stylesheet" type="text/css" media="screen, projection, print" href="slidy.css" />
<script src="slidy.js" charset="utf-8"
type="text/javascript"></script>
<!-- Highlighting: http://zenzike.com/posts/2010-10-14-highlighting-haskell-with-shjs -->
<link rel="stylesheet" type="text/css" href="sh_style.css" />
<script type="text/javascript" src="sh_main.js" > </script>
<script type="text/javascript" src="sh_haskell.js" > </script>
<script type="text/javascript" src="sh_init.js" > </script>
<!-- Custom style -->
<style type="text/css">
.cover, h1, h2, h3, h4 {
text-align: center;
}
.speakers {
margin: 1em 0;
}
.speakers li {
list-style-type: none;
margin: 0;
padding: 0;
}
@media screen {
.note { display: none; visibility: visible }
.toolbar { display:none }
}
@media print {
.note { display: block; visibility: visible; color: red; font-size: 110%; line-height: 1.5em; }
}
</style>
</head>
<body onload="sh_highlightDocument();">
<div class="slide cover">
<h1>Themenabend Haskell &amp; Yesod</h1>
<ul class="speakers">
<li>maloi &lt;mm@noexample.de&gt;</li>
<li>Astro &lt;astro@spaceboyz.net&gt;</li>
</ul>
<p>Chaos Computer Club Dresden</p>
<p>2012-08-29</p>
<p class="note">Genesis: 1990</p>
</div>
<div class="slide">
<h1>Hilfe</h1>
<ul>
<li>
haskell.org
<ul>
<li>Library documentation</li>
<li>Hackage library database</li>
</ul>
</li>
<li><a href="http://book.realworldhaskell.org/read/">Real World Haskell</a></li>
<li><a href="http://www.learnyouahaskell.com/">Learn You a Haskell</a></li>
<li>
Funktionssuche:
<ul>
<li><a href="http://holumbus.fh-wedel.de/hayoo/hayoo.html">Hayoo!</a></li>
<li><a href="http://www.haskell.org/hoogle/">Hoogle</a></li>
</ul>
</li>
</ul>
</div>
<div class="slide">
<h1>Paket-Management mit Cabal</h1>
<pre>apt-get install ghc ghc-prof cabal-install
# Updates list of known packages
cabal update
# Installs package with library profiling
cabal install -p yesod-platform</pre>
<p class="note">Glasgow Haskell Compiler</p>
<p class="note">Mit Dependencies</p>
<p class="note">-p für Profiling</p>
<p class="note">als User alles nach ~/.cabal</p>
</div>
<div class="slide cover">
<h1>Syntax</h1>
</div>
<div class="slide">
<h1>Funktionen</h1>
<pre class="sh_haskell">fac :: Integer -> Integer
fac 1 = 1
fac n = n * fac (n - 1)
</pre>
<p class="note">Fakultät</p>
<p class="note">-&gt; Implikation, rechtsassoziativ</p>
<p class="note">Pattern matching des Parameters</p>
<p class="note">Integer is bignum, Int nicht</p>
<h1>case</h1>
<pre class="sh_haskell">fac' :: Integer -> Integer
fac' n =
case n of
1 -&gt; 1
_ -&gt; n * fac (n - 1)
</pre>
<p class="note">Pattern matching</p>
<p class="note">Anonyme Variable</p>
</div>
<div class="slide">
<h1>Guards</h1>
<pre class="sh_haskell">fac'' :: Integer -> Integer
fac'' n
| n &lt;= 1 = 1
| otherwise = n * fac (n - 1)
</pre>
<p class="note">Boolean expression</p>
<h1>if then else</h1>
<pre class="sh_haskell">fac''' :: Integer -> Integer
fac''' n =
if n &lt;= 1
then 1
else n * fac (n - 1)
</pre>
<p class="note">Boolean expression</p>
</div>
<div class="slide">
<h1>Listen</h1>
<pre class="sh_haskell">$ ghci <p class="note">REPL</p>
Prelude> 2 : []
[2]
Prelude> 23 : []
[23]
Prelude> 23 : 42 : []
[23,42]
Prelude> 23 : 42 : 5 : []
[23,42,5]
Prelude> :t (:)
(:) :: a -> [a] -> [a]
</pre>
<pre class="sh_haskell">Prelude> :t map
map :: (a -> b) -> [a] -> [b]
Prelude> :t filter
filter :: (a -> Bool) -> [a] -> [a]
Prelude> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
</pre>
<p class="note">Zeige mehr containers</p>
</div>
<div class="slide">
<h1>let</h1>
<pre class="sh_haskell">fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n =
let r = fib $ n - 1
r' = fib $ n - 2
in r + r'
</pre>
<h1>where</h1>
<pre class="sh_haskell">facs = 1 : go 2
where go :: Integer -> [Integer] <span class="note">“Unterfunktion”</span>
go n = n : map (* n) (go $ n + 1)
Prelude> :t facs <span class="note">Inferred type</span>
facs :: [Integer]
Prelude> facs !! 500 <span class="note">Unendlich, lazy</span>
611288549821546144419320631496946510053040745744399613938399207781308531950094880800353720198603844213369821855051249995665852739761166777440873387784823972024431693541604632490253390402221485016448489094881275898497140756808655499992463957536774753658273522343143352081610678258299859781290947510064133164625417126214683287364306533202043771696934819148200603488701298578593288295101675375228475039194518540926501811512211084741146359555616051391364512171815202852428391973770531819587555590747725222707870915089204322125602187745638954304887403405330317436980872692673627514602895425139803517823839455807896878364709108235036658336397561844224198282288969461157203049314666899393600517284430144216183492154249948251192663554129489210224225335784718743202172831724716080857069568307715110065035130707514179942202121569852186583424945734289322385681601382444331381680307765142985006245859526451817213228740320341074735777885270328149239431528071389670544598095262337701383222299067569757970109034425702794542448640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
</pre>
<p class="note">($)</p>
</div>
<div class="slide">
<h1>Lambdas</h1>
<p class="note">Num: Interface für (+), (*)</p>
<pre class="sh_haskell">Prelude> :t (\n -> n + 1)
(\n -> n + 1) :: Num a => a -> a
Prelude> (\n -> n + 1) 22
23
</pre>
<pre class="sh_haskell">Prelude> :t (\a b c -> a * b * c)
(\a b c -> a * b * c) :: Num a => a -> a -> a -> a
</pre>
<pre class="sh_haskell">Prelude> :t (\a -> \b -> \c -> a * b * c)
(\a -> \b -> \c -> a * b * c) :: Num a => a -> a -> a -> a
</pre>
<p class="note">Umkehrschluß: Currying</p>
</div>
<div class="slide">
<h1>Currying</h1>
<pre class="sh_haskell">:t map (\a -> a + 1)
map (\a -> a + 1) :: Num b => [b] -> [b]
</pre>
<h1>Sections</h1>
<pre class="sh_haskell">Prelude> :t (+ 1)
(+ 1) :: Num a => a -> a
Prelude> map (+ 1) [1..10]
[2,3,4,5,6,7,8,9,10,11]
</pre>
</div>
<div class="slide">
<h1>Function Composition</h1>
<pre class="sh_haskell">Prelude> :i (.)
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base'
infixr 9 .
Prelude> :t (+ 5) . (* 3)
(+ 5) . (* 3) :: Num c => c -> c
Prelude> ((+ 5) . (* 3)) 23
74</pre>
</div>
<!-- Types -->
<div class="slide">
<h1>data</h1>
<p class="note">Statt NULL-Pointer</p>
<pre class="sh_haskell">data Maybe a = Nothing
| Just a
</pre>
<pre class="sh_haskell">Prelude> :t Nothing <span class="note">Constructors</span>
Nothing :: Maybe a <span class="note">Passt auf alle a</span>
Prelude> :t Just
Just :: a -> Maybe a
</pre>
<pre class="sh_haskell">data [] a = []
| a : [a]</pre>
<h1>type</h1>
<p>Typ-Alias</p>
<pre class="sh_haskell">type String = [Char]</pre>
<h1>newtype</h1>
<pre class="sh_haskell">newtype Name = Name String <span class="note">Typename = Ctor</span></pre>
<ul>
<li>Darf nur 1 Feld haben</li>
<li>Billig zur Laufzeit</li>
<li>Typsicher zur Compile-Zeit</li>
</ul>
</div>
<div class="slide">
<h1>Record-Syntax</h1>
<pre class="sh_haskell">data MeinTyp = MeinKonstruktor String Integer
Prelude> :t MeinKonstruktor
MeinKonstruktor :: String -> Integer -> MeinTyp
</pre>
<p>
Für <b>data</b> &amp; <b>newtype</b>
<span class="note">weiterhin mit nur 1 Feld</span>
</p>
<pre class="sh_haskell">data MeinTyp = MeinKonstruktor {
meinString :: String
, meinInteger :: Integer
}
<span class="note">-- Lesbare Konstruktion</span>
fnord = MeinKonstruktor {
meinString = "fnord"
, meinInteger = 23
}</pre>
<pre class="sh_haskell">Prelude> :t meinString <span class="note">Accessor</span>
meinString :: MeinTyp -> String
Prelude> :t meinInteger
meinInteger :: MeinTyp -> Integer
Prelude> meinInteger fnord
23</div>
<!-- Classes, instances -->
<div class="slide">
<h1>Classes &amp; Instances</h1>
<pre class="sh_haskell">class Show a where
show :: a -> String</pre>
<pre class="sh_haskell">instance Show MeinTyp where
show (MeinDatum n) = "MeinDatum " ++ show n</pre>
<p>Wie <i>interface</i> in Java</p>
<pre class="sh_haskell">Prelude> :i Num
class Num a where
(+) :: a -> a -> a
(*) :: a -> a -> a
(-) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
-- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'</pre>
</div>
<div class="slide">
<h1>deriving</h1>
<section style="-moz-column-count: 2; -webkit-column-count: 2">
<p>Funktioniert mit grundlegenden Datentypen:</p>
<pre class="sh_haskell">class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool</pre>
<pre class="sh_haskell">class Eq a => Ord a where
compare :: a -> a -> Ordering</pre>
<pre class="sh_haskell">class Enum a where
succ :: a -> a
pred :: a -> a</pre>
<pre class="sh_haskell">class Bounded a where
minBound :: a
maxBound :: a</pre>
<pre class="sh_haskell">class Show a where
show :: a -> String</pre>
<pre class="sh_haskell">read :: Read a => String -> a</pre>
<p>Anwendungsbeispiel:</p>
<pre class="sh_haskell">Prelude> newtype Zeigbar =
Zeig String deriving Show
Prelude> putStrLn $ show $ Zeig "Hello"
Zeig "Hello"</pre>
</section>
</div>
<div class="slide">
<h1>Module</h1>
<p>Am Beginn von Quellcode-Dateien:</p>
<pre class="sh_haskell">module MeinKram where</pre>
<pre class="sh_haskell" style="margin-top: 6em">module Main (main) where
import MainKram
import qualified Data.Text as T
fnord :: T.Text
fnord = T.pack "fnord"</pre>
</div>
<div class="slide">
<h1>Operator-Schreibweise</h1>
<pre class="sh_haskell">Prelude> :i (+)
class Num a where
(+) :: a -> a -> a
infixl 6 +
Prelude> :i (/)
class Num a => Fractional a where
(/) :: a -> a -> a
infixl 7 /
Prelude> :i (^)
(^) :: (Num a, Integral b) => a -> b -> a
infixr 8 ^</pre>
<pre class="sh_haskell">Prelude> "bar" `elem` ["foo","bar","baz"]
True</pre>
</div>
<!--
* Monads, Functors, Applicatives (maloi) (30 min)
-->
<div class="slide cover">
<h1>Functor, Applicative, Monad</h1>
</div>
<div class="slide">
<h1>Functors - Motivation</h1>
<ul class="incremental">
<li>
<p>Eine Funktion, die jeder liebt:</p>
<pre class="sh_haskell">map :: (a -> b) -> [] a -> [] b -- map :: (a -> b) -> [a] -> [b]</pre>
</li>
<li>
<p>Wie sieht so eine Funktion z.B. fuer Baeume aus?</p>
<pre class="sh_haskell">data Tree a = Leaf a | Node a (Tree a) (Tree a)</pre>
<pre class="sh_haskell">mapTree :: (a -> b) -> Tree a -> Tree b</pre>
</li>
<li>
<p>Maybe just nothing</p>
<pre class="sh_haskell">data Maybe a = Just a | Nothing</pre>
<pre class="sh_haskell">mapMaybe :: (a -> b) -> Maybe a -> Maybe b</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Functors - Motivation (2)</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">map :: (a -> b) -> [] a -> [] b</pre>
<pre class="sh_haskell">mapTree :: (a -> b) -> Tree a -> Tree b</pre>
<pre class="sh_haskell">mapMaybe :: (a -> b) -> Maybe a -> Maybe b</pre>
</li>
<li>
<p>Die Funktionen haben - bis auf Umbenennung des Typ-Konstruktors - die gleiche Signatur</p>
</li>
<li>
<p><strong>Type classes to the rescue!</strong></p>
</li>
<li>
<p>Zuvor aber ein kleiner Ausflug</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Kinds: Typen von Typen - WTF?</h1>
<ul class="incremental">
<li>
<p>In Haskell hat jeder Ausdruck einen Typ</p>
</li>
<li>
<p>Diese Typen haben wiederum "Typen" - <strong>Kinds</strong></p>
</li>
<li>
<!--<p>Jeder (monomorphe) Typ (nullstelliger Typ-Konstuktor) hat Kind <strong>*</strong></p>-->
<p><strong>*</strong> ist der Kind jedes Datentyps (nullstelliger Typ-Konstruktor)</p>
<p>Diese Typen beschreiben Werte</p>
</li>
<li>
<p><strong>k1->k2</strong> ist der Kind von einstelligen Typ-Konstruktoren, die Typen von Kind <strong>k1</strong> nehmen und Typen von Kind <strong>k2</strong> erzeugen</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Kinds: Beispiele</h1>
<ul class="incremental">
<li>
<p>Monomorph</p>
<pre class="sh_haskell">Int :: * -- z.B. 42</pre>
<pre class="sh_haskell">Maybe Int :: * -- z.B. Just 23</pre>
<pre class="sh_haskell">Int -> Int :: * -- z.B. (+23)</pre>
</li>
<li>
<p>Polymorph</p>
<pre class="sh_haskell">Maybe :: * -> *</pre>
<pre class="sh_haskell">(->) :: * -> *</pre>
<pre class="sh_haskell">(,,) :: * -> * -> *</pre>
</li>
<li>
<p>Lustig</p>
<pre class="sh_haskell">data Funny f a = Funny a (f a)
Funny :: (* -> *) -> * -> *</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Functors redux</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">map :: (a -> b) -> [] a -> [] b</pre>
<pre class="sh_haskell">mapTree :: (a -> b) -> Tree a -> Tree b</pre>
<pre class="sh_haskell">mapMaybe :: (a -> b) -> Maybe a -> Maybe b</pre>
</li>
<li>
<p>Zusammengefasst ergibt sich also folgendes:</p>
<pre class="sh_haskell">map :: (a -> b) -> f a -> f b</pre>
</li>
<li>
<p><strong>f</strong> ist demnach vom Kind <strong>* -> *</strong></p>
</li>
<li>
<p>Ist es moeglich die Funktion einmalig fuer alle Typen dieses Kinds schreiben?</p>
</li>
<li>
<p><strong>Nein</strong>, natuerlich nicht!</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - the easy type class</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">class Functor f where
fmap :: (a -> b) -> f a -> f b</pre>
</li>
<li>
<h3>Intuition<h3>
</li>
<li>
<p>Ein Functor stellt eine Art <strong>Container</strong> dar, der es ermoeglicht (mit fmap) eine Funktion (uniform) auf alle Elemente in diesem Container anzuwenden</p>
</li>
<li>
<p>Alternativ dazu kann man Functor auch als einen <strong>computational context</strong> sehen und fmap wendet eine Funktion auf einen Wert in einem Kontext an ohne diesen Kontext zu aendern</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - Kind is kind of important</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Functor Int where
fmap = ...</pre>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">
[1 of 1] Compiling Main ( 2012-03-15.lhs, interpreted )
2010-10-25.lhs:145:19:
Kind mis-match
The first argument of `Functor' should have kind `* -> *',
but `Int' has kind `*'
In the instance declaration for `Functor Int'
</pre>
</li>
<li>
<p>Wie die Fehlermeldung vermuten laesst, hat Int den Kind *</p>
<p>Functor moechte aber, dass sein erstes Argument Kind * -> * hat</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - Listen </h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Functor [] where
fmap _ [] = []
fmap g (x:xs) = g x : fmap g xs -- oder einfach fmap = map</pre>
</li>
<li>
<p>Listen sind ein gutes Beispiel fuer einen Functor, der als Container - ueber den man mappen kann - aufgefasst werden kann</p>
</li>
<li>
<p>Kann aber auch als Berechnung mit nicht-deterministischen Ergebnis gesehen werden</p>
</li>
<li>
<p>Da fmap den Kontext nicht aendert ist das Resultat wiederum nicht-deterministisch</p>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">$ ghci
Prelude> fmap (+1) [1,2,3]
[2,3,4]</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - Maybe baby </h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Functor Maybe where
fmap _ Nothing = Nothing
fmap g (Just a) = Just (g a)</pre>
</li>
<li>
<p>Maybe kann als Container gesehen werden, der ein Element haben <strong>kann</strong></p>
</li>
<li>
<p>Oder als Berechnung mit moeglichem Fehlschlag</p>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">$ ghci
Prelude> fmap (+23) (Just 19)
Just 42
Prelude> fmap (+23) Nothing
Nothing</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - Do you read me? </h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Functor ((->) r) where
fmap f g = (.) -- (\x -> f (g x))</pre>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">fmap :: (a -> b) -> f a -> f b
fmap :: (a -> b) -> ((->) r a) -> ((->) r b)
fmap :: (a -> b) -> (r -> a) -> (r -> b)</pre>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">(.) :: (b -> c) -> (a -> b) -> a -> c</pre>
</li>
<li>
<p>Container, der mit Werten vom Typ r indiziert ist</p>
</li>
<li>
<p>Berechnung, die Werte in einer (read-only) Umgebung nachschlagen kann</p>
</li>
<li>
<p><strong>(->) r</strong> wird deshalb oftmals auch als <strong>reader monad</strong> bezeichnet (mehr dazu spaeter)</p>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">$ ghci
Prelude> :m Control.Monad.Instances
Prelude Control.Monad.Instances> fmap (*3) (+10) $ 1
33
Prelude Control.Monad.Instances> (fmap (*3) (+10) $ 1) == ((*3) . (+10) $ 1)
True</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - Sin is lawlessness </h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">1. fmap id = id -- id = (\x -> x) und damit id :: a -> a
2. fmap (g . h) = (fmap g) . (fmap h)</pre>
</li>
<li>
<p>Sichern, dass fmap nur die Werte, nicht aber deren Kontext aendert</p>
</li>
<li>
<p>1. Wenn man id ueber einen Functor mapped, sollte der resultierende Functor gleich dem urspruenglichen sein</p>
</li>
<li>
<p>2. Es ist egal ob man die Komposition zweier Funktionen ueber einen Functor mapped oder erst die eine Funktion mapped und dann die andere</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Functor - I break the law </h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Functor [] where
fmap _ [] = []
fmap g (x:xs) = g x : g x : fmap g xs
</pre>
</li>
<li>
<p>Gueltige Functor Instanz</p>
</li>
<li>
<p><strong>Aber:</strong> haelt sich nicht an das erste Gesetz!</p>
<pre class="sh_haskell">fmap id [1,2,3] == [1,1,2,2,3,3]
id [1,2,3] == [1,2,3]
</pre>
</li>
<li>
<p><strong>Und:</strong> haelt sich nicht an das zweite Gesetz!</p>
<pre class="sh_haskell">fmap (id . id) [1,2,3] == [1,1,2,2,3,3]
(fmap id) . (fmap id) $ [1,2,3] == [1,1,1,1,2,2,2,2,3,3,3,3]
</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Applicative - Motivation</h1>
<ul class="incremental">
<li>
<p>fmap <strong>liftet</strong> eine (normale) Funktion zu einer Funktion, die in einem Kontext verwendet werden kann</p>
</li>
<li>
<p>Auch fmap unterliegt natuerlich wie jede andere Funktion <strong>currying/partial application</strong></p>
<pre class="sh_haskell">$ ghci
Prelude> :t fmap (+) (Just 2)
fmap (+) (Just 2) :: Num a => Maybe (a -> a)
</pre>
</li>
<li>
<p>Man kann aber mit fmap keine Funktion, die selbst in einem Kontext liegt, auf Werte in einem Kontext anwenden</p>
<pre class="sh_haskell">$ ghci
Prelude> fmap Just (+) Just 5
<interactive>:9:6:
Couldn't match expected type `t1 -> t0' with actual type `Maybe a0'
</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Applicative - Intuition incoming</h1>
<ul class="incremental">
<li>
<p><strong>[[ g x1 x2 ... xn ]]</strong> soll eine Notation fuer eine Funktionsapplikation in einem Kontext sein</p>
</li>
<li>
<p><strong>g</strong> sei nun vom Typ <strong>t1 -> t2 -> ... -> tn -> t</strong> und <strong>xi</strong> vom Typ <strong>f ti</strong> dann ist <strong>[[ g x1 x2 ... xn ]]</strong> vom Typ <strong>f t</strong> wobei f ein Functor ist</p>
</li>
<li>
<p>Das kann man sich als Funktionsapplikation auf mehrere <strong>moeglicherweise seiteneffekt habende</strong> Argumente vorstellen</p>
</li>
<li>
<p>Das ist eine Generalisierung von fmap, die ja nur auf ein einzelnes Argument in einem Kontext angewendet werden kann</p>
</li>
<li>
<p>Wendet man g auf x1 an bekommen wir eine Funktion f (t2 -> ... -> t)</p>
</li>
<li>
<p>Diese Funktion kann aber mit fmap nicht auf das naechste Argument angewendet werden - !#*"§"$</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Applicative - let me handle it</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b</pre>
</li>
<li>
<p>Oha - <*> erlaubt uns also jetzt <strong>[[ g x1 x2 ... xn ]]</strong> in Haskell umzusetzen</p>
<pre class="sh_haskell">g <$> x1 <*> x2 <*> ... <*> xn -- <$> = fmap</pre>
</li>
<li>
<p>Na das sieht doch aus wie normale Funktionsapplikation</p>
</li>
<li>
<p><strong>Applicative Programming with Effects</strong></p>
</li>
<li>
<p><strong>pure</strong> nimmt ein Argument beliebigen Typs und packt es in einen default Container oder einen effektfreien Kontext</p>
</li>
<li>
<p>Damit lassen sich auch effektfreie Argumente inmitten dieser Berechnung einbauen</p>
<pre class="sh_haskell">g <$> x1 <*> pure x2 <*> x3</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Applicative - Liste</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Applicative [] where
pure x = [x]
gs <*> xs = [ g x | g <- gs, x <- xs ]</pre>
</li>
<li>
<p>Wende jede Funktion der ersten Liste auf jedes Element der zweiten Liste an</p>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">$ ghci
Prelude Control.Applicative> (+) <$> [2,3,4] <*> pure 4
[6,7,8]
</pre>
<pre class="sh_haskell">$ ghci
Prelude Control.Applicative> (*) <$> [7,8,9] <*> [1,2]
[7,14,8,16,9,18]
</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Applicative - Liste (2)</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">newtype ZipList a = ZipList { getZipList :: [a] } -- pro Datentyp nur eine Instanz einer Typklasse</pre>
</li>
<li>
<p>Elementweise Applikation - die Listen werden zusammen gezipped</p>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">instance Applicative ZipList where
pure = ZipList (repeat x)
(ZipList gs) <*> (ZipList xs) = ZipList (zipWith ($) gs xs)</pre>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">$ ghci
Prelude Control.Applicative> getZipList $ (mod) <$> ZipList [9,27,56] <*> ZipList [1..10]
[0,1,2]
</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Applicative - Gesetze</h1>
<ul class="incremental">
<li>
<p>Es gibt mehre Gesetze aber wichtig fuer das Verstaendnis ist nur eins:</p>
<p>Wie haengt Applicative mit Functor zusammen?</p>
</li>
<li style="list-style-type: none;">
<pre class="sh_haskell">fmap g x = pure g <*> x</pre>
<p>Eine normale Funktion die ueber einen Kontext x gemapped wird ist aequivalent dazu diese Funktion in den Kontext zu liften und sie dann mit <*> auf x anzuwenden</p>
</li>
<li>
<p>Die restlichen Gesetze sichern nur, dass sich pure so verhaelt wie man es erwartet</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Monad - Easy</h1>
<h3>A monad is just a monoid in the category of endofunctors, what's the problem?</h3>
</div>
<div class="slide">
<h1>Monad - Just another type class</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
m >> n = m >>= \_ -> n
fail :: String -> m a</pre>
</li>
<li>
<p><strong>return</strong> entspricht pure aus Applicative: <strong>return IS pure</strong></p>
<p><strong>Jede Monad ist ein Applicative</strong> Warum aber kein Constraint wie bei Applicative?</p>
</li>
<li>
<p>Monads werden fuer IO in Haskell verwendet und waren vor Applicative in Haskell</p>
<p>Nachtraeglich zu verlangen ein Monad muss ein Applicative sein, wuerde viel Code kaputt machen etc.</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Monad - Just another type class (2)</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
m >> n = m >>= \_ -> n
fail :: String -> m a</pre>
</li>
<li>
<p><strong>(>>)</strong> ist ein Spezialfall von (>>=) und es gibt eine Default Implementierung</p>
<p>(>>) ignoeriert das Ergebnis einer Berechnung aber nicht ihre Effekte</p>
</li>
<li>
<p><strong>fail</strong> ist ein Hack und gehoert eigtl. nicht in Monad - daher der Name? ;)</p>
<pre class="sh_haskell">do (x:xs) <- foo -- Pattern matching fails wenn foo eine leere Liste produziert -> fail
bar x</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Monad - Just another type class (3)</h1>
<ul class="incremental">
<li style="list-style-type: none;">
<pre class="sh_haskell">class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
m >> n = m >>= \_ -> n</pre>
</li>
<li>
<p><strong>(>>=)</strong> nennt man <strong>bind</strong> und ermoeglicht eine Berechnung abhaengig von dem Ergebnis einer davor stattfinden Berechnung zu machen</p>
</li>
<li>
<p>Looking at the types</p>
<pre>(>>=) :: m a -> (a -> m b) -> m b</pre>
</li>
<li>
<p>Das zweite Argument, (a -> m b), ist eine Berechnung, die als Eingabe das Ergebnis der ersten Berechnung nimmt und dann die Berechnung m b durchfuehren kann</p>
<p><strong>x >>= k</strong> ist also eine Berechnung, die x ausfuehrt und dann das Ergebnix von x nutzt um zu <strong>entscheiden</strong> welche Berechnung es als zweites ausfuehrt und nimmt das Ergebnis dieser (zweiten) Berechnung als Ergebnis der gesamten Berechnung</p>
</li>
</ul>
</div>
<div class="slide">
<h1>Monad - Warum reicht Applicative nicht</h1>
<ul class="incremental">
<li>
<p>Kann man (>>=) nur mit fmap, pure und <*> darstellen?</p>
</li>
<li>
<p>Wenn x vom Typ m a ist und k vom Typ a -> m b, kann man nur k auf x anwenden</p>
<p>Das geht natuerlich nur mit fmap und fmap k hat dann den Typ <strong>m a -> m (m b)</strong></p>
<p>Wenn man das jetzt auf m a anwendet bekommt man etwas mit dem Typ <strong>m (m b)</strong></p>
<p>Hier ist Schluss - Wir wollen aber etwas vom Typ <strong>m b</strong>
</li>
<li>
<p>Wir brauchen also eine Funktion, die mehrere <strong>m</strong>s zusammenfuehrt</p>
<pre class="sh_haskell">join :: m (m a) -> m a</pre>
</li>
<li>
<p>Man kann Monad auch wie folgt definieren: (Categorytheorists can dance now)</p>
<pre class="sh_haskell">class Applicative m => Monad' m where
join :: m (m a) -> m a</pre>
</li>
</ul>
</div>
<div class="slide">
<h1>Real World Haskell, Chapter 16: Parsec</h1>
<pre class="sh_haskell">import Text.ParserCombinators.Parsec
{- A CSV file contains 0 or more lines, each of which is terminated
by the end-of-line character (eol). -}
csvFile :: GenParser Char st [[String]]
csvFile =
do result &lt;- many line
eof
return result
-- Each line contains 1 or more cells, separated by a comma
line :: GenParser Char st [String]
line =
do result &lt;- cells
eol -- end of line
return result
-- Build up a list of cells. Try to parse the first cell, then figure out
-- what ends the cell.
cells :: GenParser Char st [String]
cells =
do first &lt;- cellContent
next &lt;- remainingCells
return (first : next)
-- The cell either ends with a comma, indicating that 1 or more cells follow,
-- or it doesn't, indicating that we're at the end of the cells for this line
remainingCells :: GenParser Char st [String]
remainingCells =
(char ',' >> cells) -- Found comma? More cells coming
&lt;|> (return []) -- No comma? Return [], no more cells
-- Each cell contains 0 or more characters, which must not be a comma or
-- EOL
cellContent :: GenParser Char st String
cellContent =
many (noneOf ",\n")
-- The end of line character is \n
eol :: GenParser Char st Char
eol = char '\n'
parseCSV :: String -> Either ParseError [[String]]
parseCSV input = parse csvFile "(unknown)" input</pre>
</div>
<div class="slide">
<h1>Real World Haskell, Chapter 16: Parsec</h1>
<pre class="sh_haskell">import Text.ParserCombinators.Parsec
csvFile = endBy line eol
line = sepBy cell (char ',')
cell = many (noneOf ",\n")
eol = char '\n'
parseCSV :: String -> Either ParseError [[String]]
parseCSV input = parse csvFile "(unknown)" input
</pre>
</div>
<!--
* Template Haskell (maloi) (20min)
-->
<div class="slide">
<h1>Profiling</h1>
<p class="incremental">Beispiel aus Real World Haskell, Chapter 25:</p>
<div class="incremental">
<pre class="sh_haskell">import System.Environment
import Text.Printf
main = do
[d] &lt;- map read `fmap` getArgs
printf "%f\n" (mean [1..d])
mean :: [Double] -> Double
mean xs = sum xs / fromIntegral (length xs)</pre>
</div>
<pre class="incremental">% time ./prof1 1e5
50000.5
./prof1 1e5 0.03s user 0.01s system 89% cpu 0.049 total
% time ./prof1 1e6
500000.5
./prof1 1e6 0.31s user 0.09s system 98% cpu 0.400 total
% time ./prof1 1e7
5000000.5
./prof1 1e7 2.95s user 0.60s system 97% cpu 3.626 total
% time ./prof1 1e8
zsh: killed ./prof1 1e8
./prof1 1e8 6.72s user 2.71s system 91% cpu 10.368 total</pre>
<p class="note">sum iteriert durch, Liste kann aber nicht GCed
werden, da length sie auch noch benötigt</p>
</div>
<div class="slide">
<h1>Time Profiling</h1>
<pre>% ghc --make -O2 -prof -caf-all -auto-all prof1
% ./prof1 1e7 +RTS -p <span class="note">-P, -pa</span>
5000000.5
% cat prof1.prof
Wed Aug 29 02:04 2012 Time and Allocation Profiling Report (Final)
prof1 +RTS -p -RTS 1e7
total time = 1.74 secs (1738 ticks @ 1000 us, 1 processor)
total alloc = 1,680,119,104 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
main Main 86.6 100.0
mean Main 13.4 0.0
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 55 0 0.0 0.0 100.0 100.0
main Main 111 0 86.6 100.0 100.0 100.0
mean Main 113 1 13.4 0.0 13.4 0.0
CAF:main1 Main 108 0 0.0 0.0 0.0 0.0
main Main 110 1 0.0 0.0 0.0 0.0
CAF:main3 Main 107 0 0.0 0.0 0.0 0.0
main Main 112 0 0.0 0.0 0.0 0.0
CAF GHC.Conc.Signal 101 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding 100 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Handle.FD 99 0 0.0 0.0 0.0 0.0
CAF Text.Read.Lex 95 0 0.0 0.0 0.0 0.0
CAF GHC.Float 90 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding.Iconv 82 0 0.0 0.0 0.0 0.0</pre>
</div>
<div class="slide">
<h1>Space Profiling</h1>
<pre>% ghc --make -rtsopts -prof -caf-all -auto-all prof1
% ./prof1 1e6 +RTS -K256M -hc -i0.01
% hp2ps -c prof1.hp</pre>
<img src="prof1-hc.svg"/>
</div>
<div class="slide">
<h1>Space Profiling</h1>
<pre>% ./prof1 1e6 +RTS -K256M -hy -i0.01
% hp2ps -c prof1.hp</pre>
<img src="prof1-hy.svg"/>
<p class="note">Auch: GHC Core output</p>
</div>
<div class="slide">
<h1>Strictness</h1>
<pre class="sh_haskell">import System.Environment
import Text.Printf
import Data.List (foldl')
main = do
[d] &lt;- map read `fmap` getArgs
printf "%f\n" (mean [1..d])
mean :: [Double] -> Double
mean xs = s / fromIntegral n
where
(n, s) = foldl' k (0, 0) xs
k (n, s) x = n `seq` s `seq` (n+1, s+x)</pre>
<pre>% ./prof2 1e8 +RTS -sstderr
50000000.5
53,600,200,824 bytes allocated in the heap
31,319,592 bytes copied during GC
62,720 bytes maximum residency (1 sample(s))
26,816 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 102796 colls, 0 par 0.66s 0.66s 0.0000s 0.0003s
Gen 1 1 colls, 0 par 0.00s 0.00s 0.0006s 0.0006s
INIT time 0.00s ( 0.00s elapsed)
MUT time 32.71s ( 32.84s elapsed)
GC time 0.66s ( 0.66s elapsed)
RP time 0.00s ( 0.00s elapsed)
PROF time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 33.38s ( 33.50s elapsed)
%GC time 2.0% (2.0% elapsed)
Alloc rate 1,638,238,233 bytes per MUT second
Productivity 98.0% of total user, 97.7% of total elapsed
</pre>
<p class="note">In RWH: unboxed strict fields, fusion w/o heap alloc</p>
</div>
<!--
* Parallel Haskell (maloi) (10min)
* Foreign (astro) (5min)
-->
<div class="slide cover">
<h1>Strictness</h1>
<p class="note">Stream processing</p>
<p class="note">Gegen unkontrollierte Lazyness</p>
</div>
<div class="slide">
<h1>ResourceT m a</h1>
<pre class="sh_haskell">Control.Monad.Trans.Resource> :i MonadResource
class (MonadThrow m, MonadUnsafeIO m,
Control.Monad.IO.Class.MonadIO m,
Control.Applicative.Applicative m) => MonadResource m where
register :: IO () -> m ReleaseKey
release :: ReleaseKey -> m ()
allocate :: IO a -> (a -> IO ()) -> m (ReleaseKey, a)
resourceMask ::
((forall a. ResourceT IO a -> ResourceT IO a) -> ResourceT IO b)
-> m b
-- Defined in `Control.Monad.Trans.Resource'
</pre>
</div>
<div class="slide">
<h1>Pipe Type</h1>
<pre class="sh_haskell">Data.Conduit> :i Pipe
data Pipe l i o u m r
= Data.Conduit.Internal.HaveOutput (Pipe l i o u m r) (m ()) o
| Data.Conduit.Internal.NeedInput (i -> Pipe l i o u m r)
(u -> Pipe l i o u m r)
| Data.Conduit.Internal.Done r
| Data.Conduit.Internal.PipeM (m (Pipe l i o u m r))
| Data.Conduit.Internal.Leftover (Pipe l i o u m r) l
instance Monad m => Monad (Pipe l i o u m)
instance Monad m => Functor (Pipe l i o u m)</pre>
<pre class="sh_haskell">Data.Conduit> :i Conduit
type Conduit i m o = Pipe i i o () m ()</pre>
<pre class="sh_haskell">Data.Conduit> :i Source
type Source m o = Pipe () () o () m ()</pre>
<pre class="sh_haskell">Data.Conduit> :i Sink
type Sink i m r = Pipe i i Data.Void.Void () m r</pre>
</pre>
</div>
<div class="slide">
<h1>Conduit Operators</h1>
<pre class="sh_haskell">Data.Conduit> :i ($$)
($$) :: Monad m => Source m a -> Sink a m b -> m b
infixr 0 $$</pre>
<pre class="sh_haskell">Data.Conduit> :i (=$=)
(=$=) :: Monad m => Conduit a m b -> Conduit b m c -> Conduit a m c
infixr 2 =$=</pre>
<pre class="sh_haskell">Data.Conduit> :i ($=)
($=) :: Monad m => Source m a -> Conduit a m b -> Source m b
infixl 1 $=</pre>
<pre class="sh_haskell">Data.Conduit> :i (=$)
(=$) :: Monad m => Conduit a m b -> Sink b m c -> Sink a m c
infixr 2 =$</pre>
</div>
<div class="slide">
<h1>Beispiel: cp</h1>
<pre class="sh_haskell">module Main (main) where
import Data.Conduit
import qualified Data.Conduit.Binary as CB
import System.Environment (getArgs)
copyFile :: FilePath -> FilePath -> IO ()
copyFile src dest = runResourceT $ CB.sourceFile src $$ CB.sinkFile dest
main = getArgs >>= \[src, dest] ->
copyFile src dest</pre>
</div>
<div class="slide">
<h1>Beispiel: Bytes zählen</h1>
<pre class="sh_haskell">{-# LANGUAGE BangPatterns #-}
module Main (main) where
import Data.Conduit
import qualified Data.Conduit.Binary as CB
import System.Environment (getArgs)
import Control.Monad.Trans
import qualified Data.ByteString as B
copyFile :: FilePath -> FilePath -> IO ()
copyFile src dest = runResourceT $
CB.sourceFile src $=
countBytes $$
CB.sinkFile dest
main = getArgs >>= \[src, dest] ->
copyFile src dest
countBytes :: MonadIO m => Conduit B.ByteString m B.ByteString
countBytes =
let loop !total =
do mBuf &lt;- await
case mBuf of
Just buf ->
do yield buf
loop $ total + B.length buf
Nothing ->
do liftIO $ putStrLn $
"Transferred " ++
show total ++
" bytes"
return ()
in loop 0</pre>
</div>
<div class="slide">
<h1>WAI: Web Application Interface</h1>
<pre class="sh_haskell">Network.Wai> :i Application
type Application =
Request -> ResourceT IO Response</pre>
<pre class="sh_haskell">Network.Wai> :i Middleware
type Middleware = Application -> Application</pre>
<pre class="sh_haskell">Network.Wai.Middleware.Autohead> :t autohead
autohead :: Network.Wai.Middleware
Network.Wai.Middleware.Gzip> :t gzip
gzip :: GzipSettings -> Network.Wai.Middleware</div>
</div>
<div class="slide">
<h1>WAI Request &amp; Response</h1>
<pre class="sh_haskell">Prelude Network.Wai> :i Request
data Request
= Request {requestMethod :: Method,
httpVersion :: HttpVersion,
rawPathInfo :: ByteString,
rawQueryString :: ByteString,
serverName :: ByteString,
serverPort :: Int,
requestHeaders :: RequestHeaders,
isSecure :: Bool,
remoteHost :: SockAddr,
pathInfo :: [Text],
queryString :: Query,
requestBody :: Source (ResourceT IO) ByteString,
vault :: Vault}</pre>
<pre class="sh_haskell">Prelude Network.Wai> :i Response
data Response
= ResponseFile Status ResponseHeaders FilePath (Maybe FilePart)
| ResponseBuilder Status ResponseHeaders Builder
| ResponseSource Status ResponseHeaders (Source (ResourceT IO) (Flush Builder))</pre>
</div>
<div class="slide">
<h1>WAI: Motivation</h1>
<p>Einheit: Requests/second</p>
<img src="preliminary-warp-cross-language-benchmarks.png"/>
<p>
<a href="http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks">www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks</a>
</p>
</div>
<!--
mtl
lists: monomorph
* Yesod:
 * Conduits (astro) (15 min)
*
 * Wai (astro) (10 min)
* Frontend, middlewares, backend (graphic)
 * Handlers & Hamlet (astro) (25 min)
* App construction
* Routing
* RESTful content
* Hamlet, Whamlet
* I18N
 * Persistent (maloi) (10min)
-->
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.