name: inverse layout: true class: center, middle, inverse --- # Haskell Funksjonell programmering hardt og godt .footnote[Slides og kode ligger på [Github](https://github.com/teodorlu/haskell-hardt-og-godt)] --- layout: false ## Agenda 1. Oppsett 2. Funksjoner 3. Typer 3. Haskell 2. Lister 4. Monader 5. IO --- template: inverse # Oppsett Editor og kompilator ??? - Kode eller lene seg tilbake - Hva dere trenger av verktøy for å - Skrive Haskell - Kjøre Haskell - Haskell har pen syntaks, IDE trengs i mindre grad --- ## Editor [Sublime Text 3](http://www.sublimetext.com/3) er en god teksteditor og støtter Haskell-syntax ut av boksen. Last ned: ``http://www.sublimetext.com/3`` ... men til og med Notepad går fint. -- ## Kompilator - Det fornuftige valget er Glascow Haskell Compiler (GHC) - Last ned på ``http://www.haskell.org/ghc/`` - Du skal nå kunne kjøre ``gch`` ``` > ghc ghc: no input files Usage: For basic information, try the `--help' option. ``` - ... og ``ghci`` fra kommandolinja ``` > ghci GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help ... Prelude> ``` - Hvis du fikk en liknende beskjed, er alt klart. --- name: funksjoner template: inverse # Funksjoner i haskell Rett på sak. --- ## Funksjonssyntax ```haskell addOne x = x + 1 -- funksjonsnavn input output -- (Kommentarer starter med --) ``` - Lagre i ``functions.hs`` og last inn i GHCi ```html ~ ghci GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Prelude> :l functions.hs [1 of 1] Compiling Main ( functions.hs, interpreted ) Ok, modules loaded: Main. Main> addOne 1 2 ``` Merk: ingen paranteser! --- ## Noen funksjoner til ```haskell add2 x y = x + y add3 x y z = x + add2 y z hypothenusLength a b = sqrt (a*a + b*b) ``` -- ## Funksjonssyntax: oppgave Implementér denne java-funksjonen i Haskell: ```java int squaredSum(int x, int y) { return x * x + y * y } ``` Samme i Matlab: ```matlab function s = squaredSum(x, y) s = x*x + y*y end ``` -- ```haskell squaredSum x y = x*x + y*y ``` Konsist, ikke sant? --- ## Mønstergjenkjenning ```haskell isTeodor "Teodor" = True isTeodor navn = False ``` -- ... men vi bruker jo ikke ``navn``. ``_`` matcher alt. ```haskell isTeodor "Teodor" = True isTeodor _ = False ``` -- *Haskell bruker det første mønsteret som matcher.* --- ## Mønstergjenkjenning: oppgave Lag funksjonen ``lucky``. Returnér ``"LUCKY NUMBER 7!"`` om du får inn ``7``, ``"Too bad :("`` ellers. -- ```haskell lucky 7 = "LUCKY NUMBER 7!" lucky _ = "Too bad :(" ``` --- ## Anonyme funksjoner Kommer fra labdakalkyle (lambda calculus, λ-calculus). Kan lage funksjoner uten å gi dem navn, og bruke dem over alt. ```haskell addOne = \x -> x + 1 ``` -- Prøv i GHCi: ```html Main> (\x -> x + 1) 2 3 ``` --- ## Å finne typen til funksjoner Kan vi gjøre i GHCi. Funksjonene fra forrige kapittel har jeg lagra i functions.hs. ``:l`` er kort for ``:load``, som lar deg bruke funksjoner og typer fra ei fil. ```html Prelude> :l functions.hs ... Main> addOne 4 5 ``` -- ``:t`` er kort for ``:type``. ```html Main> :t addOne addOne :: Int -> Int Main> :t \x -> x + 1 \x -> x + 1 :: Num a => a -> a ``` --- ## Typen til funksjoner ```haskell addOne :: Int -> Int addOne x = x + 1 add :: Int -> Int -> Int add x y = x + y ``` Hvordan gir dette mening? -- En annen måte å skrive ``add``: ```haskell add :: Int -> (Int -> Int) add x y = x + y ``` ``add`` returnerer en funksjon! --- ## Currying; *å legge ved argumenter* ```html Main> :t add add :: Integer -> Integer -> Integer Main> :t (add 1) (add 1) :: Integer -> Integer Main> :t ((add 1) 2) ((add 1) 2) :: Integer ``` Hver gang vi spesifieserer et argument, får vi en ny funksjon! Når vi ikke har igjen flere argumenter, får vi ut en verdi (Integer). --- ## Operatorer - ``+``, ``-``, ``*``, ``/``, ``^`` er noen operatorer i Haskell. - Operatorene er vanlige funksjoner som skrives *mellom* argumetene sine. - Setter vi parantes rundt operatoren, kan vi bruke den som en funksjon. ```html Main> 1 + 2 3 Main> (+) 1 2 3 ``` -- Rekkefølgen blir bevart ```html Prelude> (2/) 5 0.4 Prelude> (/2) 5 2.5 ``` --- ## Funksjoner som verdier *Høyere ordens programmering* ```haskell applyToBothThenAdd f x y = add (f x) (f y) ``` --- ## Funksjoner som verdier: oppgave 1 Lag funksjonen ``doTwice`` som tar inn en funksjon ``f`` og et argument ``x``. Returner f(f(x)). Eksempelbruk: ```haskell > doTwice (+1) 3 5 > doTwice (*2) 3 12 ``` -- ```haskell doTwice f x = f (f x) ``` -- Hva med ``do3times``? -- ```haskell do3times f x = f (f (f x)) ``` --- ## Funksjoner som verdier: oppgave 2 Lag funksjonen ``doNTimes``. ```haskell doNTimes 0 f x = x doNTimes n f x = ??? ``` Prøv å uttrykke steg ``n`` ved steg `n-1`. -- ```haskell doNTimes 0 f x = x doNTimes n f x = doNTimes (n-1) f (f x) ``` --- name: compose ## Funksjoner som verdier: oppgave 3 - Lag funksjonen ``compose`` som tar inn to funksjoner ``f`` og ``g`` og et argument ``x``, og returnerer f(g(x)). - Definér ``doTwice`` med ``compose``. - Definér ``plusTwoTimesThree`` ved hjelp av ``compose``. -- ```haskell compose f g x = f (g x) ``` -- ```haskell doTwice' f = compose f f doTwice'' f x = (compose f f) x -- Alternativt ``` -- ```haskell plusTwoTimesThree = compose (*3) (+2) ``` -- Hva er typene til ``compose`` og ``doTwice``? ??? - Finne fram manuelt: hva må vi kreve? - Sjekke GHCi -- ```haskell compose :: (b -> c) -> (a -> b) -> a -> c compose f g x = f (g x) doTwice :: (a -> a) -> a -> a doTwice f = compose f f ``` --- name: om-haskell template:inverse 1 # Programmeringsspråket Haskell Ulikt alt annet du har sett -- .footnote[... for andre enn matematikere] --- ## Ren funksjonell programmering - Funksjoner i imperativ programmering (Java, C, Python, ...) - er noe som kan hente informasjon fra argumentene sine og verden, gjøre noe med argumentene sine og verden, før den returnerer noe -- - Altså en *gruppering av instruksjoner (befalinger)* .footnote[**imperativ** *m1* ``av lat imperare 'befale, byde'`` [[1](https://ordbokene.no/bm/25912)]] -- ```java int getAge(Person person) { Rocket rocket = RocketStore.nextAvailable(); rocket.load(person); kill(person); launch(rocket); return person.age; } ``` --- ## Ren funksjonell programmering - Funksjoner i matematikk - er en relasjon fra en mengde (type) til en annen - **Og ingenting annet!** -- - Funksjoner i Haskell er matematiske funksjoner -- ```haskell addOne :: Int -> Int -- f er en relasjon fra heltall til heltall -- ... og ingenting annet! ``` - Imperative språk: funksjoner *gjør* noe - Deklarative språk: funksjoner *er* noe --- ## Sterk automatisk typing -- ### Sterk typing - Kompilatoren *veit* typen - Java, C#, C++, Scala, Haskell ```java double circleArea(double radius) { return Math.pi * radius * radius; } ``` -- ### Automatisk typing - Kompilatoren kan regne ut typen på egen hånd: trenger ikke spesifiseres! - C# (delvis), Scala, Haskell ```haskell circleArea r = pi * r * r -- Som en vanlig funksjon circleArea' = \r -> pi * r * r -- Som en anonym funksjon ``` --- ## Lazy Verdier evalueres først når de trengs ```haskell squares = map (^2) [1..] tenSquares = take 10 squares ``` ... der take n xs returnerer de n første elemenene fra xs ```haskell take :: Int -> [a] -> [a] ``` --- template: inverse name: lister # Lister .footnote[<3] --- ## Å lage lister i Haskell ```html Main> [] [] Main> [1,2,3,4] [1,2,3,4] ``` ``String`` i Haskell er en liste av ``Char``: ```html Main> ['a','b','c'] "abc" *Main> :t ['a','b','c'] ['a','b','c'] :: [Char] *Main> :t "abc" "abc" :: [Char] ``` --- ## "Endring" lister i Haskell Legg til elementer med ``:`` og pattern matching ```html Main> 0:[1,2,3,4] [0,1,2,3,4] ``` Hent ut elemnter med ``:`` ```html Main> let (head:tail) = [1,2,3,4] Main> head 1 Main> tail [2,3,4] ``` --- ## Oppgave: telling! Lag funksjonen ``countToTen`` som teller fra ``x`` til 10. Anta ``x <= 10``. ```html Main> countToTen 3 [3,4,5,6,7,8,9,10] Main> countToTen 9 [9,10] ``` -- ```haskell countToTen 10 = [10] countToTen n = n : countToTen (n+1) ``` Rekursjon! --- ## Oppgave: telling! ```haskell countToTen 10 = [10] countToTen n = n : countToTen (n+1) ``` -- fungerer som følger: ``countToTen 7 = 7 : countToTen 8`` -- ``countToTen 7 = 7 : 8 : countToTen 9`` -- ``countToTen 7 = 7 : 8 : 9 : countToTen 10`` -- ``countToTen 7 = 7 : 8 : 9 : [10]`` -- ``countToTen 7 = 7 : 8 : [9, 10]`` -- ``countToTen 7 = 7 : [8, 9, 10]`` -- ``countToTen 7 = [7, 8, 9, 10]`` --- ## Guards Hva om jeg ``countToTen 11`` ... ? -- Bedre versjon: ```haskell countToTen' n | n > 10 = [] | otherwise = n : countToTen' (n+1) ``` --- ## Oppgave: bartender Lag en funksjon ```haskell bartender :: Int -> String ``` som fordeler lovlig så folk blir fullest mulig. ```html Main> bartender 5 "Milk" Main> bartender 20 "Whisky" Main> bartender 18 "Beer" ``` --- ## Oppgave: bartender ```haskell bartender n | n < 18 = "Milk" | n < 20 = "Beer" | otherwise = "Whisky" ``` --- ## Funksjoner som tar inn lister? Eksempel: bruke en funksjon på alle elementene i ei liste. Denne finnes allerede, og heter ``map``. Vi lager vår egen, og kaller den ``map'``, som må se noe sånt ut: ```haskell map' :: (a -> b) -> [a] -> [b] map' f xs = ? ``` -- Hva skjer om vi får inn ei tom liste? ```haskell map' f [] = [] ``` -- Da kan vi lage en som matcher lister med ett element eller flere: ```haskell map' f (x:xs) = f x : map' f xs ``` ??? Hvorfor trenger vi parantesen? --- ## Oppgave: filtrere elementer fra liste ```haskell filter' :: (a -> Bool) -> [a] -> [a] filter' cond xs = ? ``` ```html Main> filter' (==42) [3,5,2,7,42,6] [42] Main> filter' (>9000) [10,100,1000,10000] [10000] *Main> let minimumAcceptablePartneAge = \age -> age / 2 + 7 *Main> filter' (\y -> y > minimumAcceptablePartneAge 23) [13..25] [19.0,20.0,21.0,22.0,23.0,24.0,25.0] ``` "Hvorfor alder / 2 + 7?" Se [xkcd 314] [xkcd 314]: https://xkcd.com/314/ -- . Tilbake på sak. Kanskje har dere fått noe sånt? ```haskell filter' cond [] = [] filter' cond (x:xs) | cond x = x : filter' cond xs | otherwise = filter' cond xs ``` --- name: datatyper template: inverse # Datatyper --- ## "Jeg kan java, der har vi mange klasser, dette blir lett!" -- Gløm hva dere har lært i Java. -- Virkelig, gløm det. -- Typesystemet til Haskell er overlegent i forhold. --- background-image: url(images/languages-as-seen-by.png) --- background-image: url(images/languages-as-seen-by-highlight.png) --- ## En enkel algebraisk datatype Betydning: *sammensatte datatyper*. ```haskell data TrafficLightState = RedLight | YellowLight | GreenLight deriving (Show, Eq) ``` .footnote[``deriving (Show, Eq)`` betyr at Haskell finner ut hvordan denne typen skal vises (``Show``) og sjekkes for likhet (``Eq``). Denne jobben skal vi gjøre selv seinere.] Et trafikklys er enten rødt, gult eller grønt. -- ### Store bokstaver har noe å si! - Typer må ha stor forbokstav - ``Int`` - ``String`` - ``TrafficLightState`` - Verdier må ha liten forbokstav - ``filter`` - ``pi`` - ``n`` --- ## Oppgave: en enkel algebraisk datatype ```haskell data TrafficLightState = RedLight | YellowLight | GreenLight deriving (Show, Eq) data CarSpeed = FastDriving | SlowDriving deriving (Show, Eq) ``` Lag ``shouldStop`` som best etter trafikkreglene: ```haskell shouldStop :: CarSpeed -> TrafficLightState -> Bool ``` -- ```haskell shouldStop :: CarSpeed -> TrafficLightState -> Bool shouldStop _ RedLight = True shouldStop _ GreenLight = False shouldStop speed YellowLight = speed == SlowDriving ``` --- ## Algebraiske datatyper med argumenter ```haskell data Vector3 = Vector3 Double Double Double deriving (Show, Eq) ``` Litt bruk: ```html Main> Vector3 3 4 5 Vector3 3.0 4.0 5.0 Main> let v = Vector3 3 4 5 Main> let Vector3 x y z = v Main> (x, y, z) (3.0,4.0,5.0) ``` --- ## Algebraiske datatyper med argumenter: oppgave ```haskell data Vector3 = Vector3 Double Double Double deriving (Show, Eq) ``` Lag en funksjon for å gange en skalar med en vektor ```haskell scalarMult3 :: Double -> Vector3 -> Vector3 ``` Eksempelbruk: ```html Main> scalarMult3 2 (Vector3 2 1 2) Vector3 4.0 2.0 4.0 Main> scalarMult3 (-3) (Vector3 2 1 2) Vector3 (-6.0) (-3.0) (-6.0) ``` -- Kanskje gjorde dere noe sånt, ```haskell scalarMult3 :: Double -> Vector3 -> Vector3 scalarMult3 a (Vector3 x y z) = Vector3 (a*x) (a*y) (a*z) ``` --- ## Algebraiske datatyper med argumenter: oppgave og en funksjon for å legge sammen to vektorer ```haskell vector3Add :: Vector3 -> Vector3 -> Vector3 ``` Eksempelbruk: ```html Main> vector3Add (Vector3 2 1 2) (Vector3 4 2 0) Vector3 6.0 3.0 2.0 Main> vector3Add (Vector3 3 4 0) (Vector3 2 3 1) Vector3 5.0 7.0 1.0 ``` -- Mulig løsning: ```haskell vector3Add :: Vector3 -> Vector3 -> Vector3 vector3Add (Vector3 x1 y1 z1) (Vector3 x2 y2 z2) = Vector3 (x1+x2) (y1+y2) (z1+z2) ``` --- ## Records: felter har navn ```haskell data PersonInfo = PersonInfo { name :: String, age :: Int } deriving (Show, Eq) ``` -- Litt bruk: ```html Main> let teodor = PersonInfo {name = "Teodor", age = 23} Main> teodor PersonInfo {name = "Teodor", age = 23} Main> :t PersonInfo PersonInfo :: String -> Int -> PersonInfo Main> let frans = PersonInfo "Frans" 24 Main> frans PersonInfo {name = "Frans", age = 24} ``` --- ## Records: felter har navn ```haskell data PersonInfo = PersonInfo { name :: String, age :: Int } deriving (Show, Eq) ``` ``name`` og ``age`` er funksjoner Haskell lager for oss: ```html Main> name teodor "Teodor" Main> age frans 24 Main> :t name name :: PersonInfo -> String Main> :t age age :: PersonInfo -> Int ``` --- name: typeklasser ## Typeklasser Egenskaper ved datatyper. - ``Eq``: typer som kan sjekkes for likhet - ``Show``: typer som kan vises - ``Num``: typer som kan oppføre seg som tall - ``Int``, ``Double``, ``Float``, ..., er instanser -- .footnote[Har ingenting med klasser i Java å gjøre!] --- ## En typeklasse for alle vektorer ... ikke bare de som er i 3D! -- ```haskell class Vector a where vectorAdd :: a -> a -> a scalarMult :: Double -> a -> a ``` *Hvis du kan gjøre ``vectorAdd`` og ``scalarMult`` på **a**, er **a** en instans av Vector* -- ... så kan vi si at Vector3 er en Vector. Da må vi si hvordan Vector3 implementerer ``vectorAdd`` og ``scalarMult``. ```haskell instance Vector Vector3 where vectorAdd = vector3Add scalarMult = scalarMult3 ``` --- ## Tilsvarende for todimensjonale vektorer ```haskell data Vector2 = Vector2 Double Double deriving (Show, Eq) vector2Add :: Vector2 -> Vector2 -> Vector2 vector2Add (Vector2 x1 y1) (Vector2 x2 y2) = Vector2 (x1+x2) (y1+y2) scalarMult2 :: Double -> Vector2 -> Vector2 scalarMult2 a (Vector2 x y) = Vector2 (a*x) (a*y) instance Vector Vector2 where vectorAdd = vector2Add scalarMult = scalarMult2 ``` --- template: inverse name: monader # Monader Nå kommer de snikende. --- name: kanskje ## Kanskje? ```haskell atIndex :: [a] -> Int -> a atIndex xs index = ? ``` -- Veldefinert dersom ``index >= 0 && index < length xs``. Hva gjør vi ellers? Forslag? -- ```haskell atIndex :: [a] -> Int -> Kanskje a ``` --- ## Kanskje. ```haskell data Kanskje a = Bare a | Tull deriving (Show, Eq) ``` *``Kanskje a`` betyr at vi kan bytte ut ``a`` med typen vi vil.* Vi kan ha ``Kanskje Int``, ``Kanskje String`` eller ``Kanskje TrafficLightState``. -- ```haskell atIndex :: [a] -> Int -> Kanskje a atIndex [] _ = Tull atIndex (x:xs) i | i < 0 = Tull | i == 0 = Bare x | otherwise = atIndex xs (i-1) ``` --- ## Personer Mulighet: ```haskell data Person = Person { name :: String, father :: Person, mother :: Person } ``` -- ```haskell teodor = Person { name = "Teodor", father = Person { name = "Tore", father = Person { name = "Leif", father = ???, mother = ??? }, mother = ??? }, mother = ??? } ``` *Hva i all verden gjør vi når vi ikke veit hvem foreldrene er?* Er ikke adoptivbarn personer? --- ## Personer (2) ```haskell data Person = Person { name :: String, father :: Kanskje Person, mother :: Kanskje Person } deriving (Show, Eq) ``` -- ```haskell leif = Person "Leif" Tull Tull tore = Person "Tore" (Bare leif) Tull alf = Person "Alf" Tull Tull hanne = Person "Hanne" (Bare alf) Tull teodor = Person "Teodor" (Bare tore) (Bare hanne) ``` --- ## Bestefedre ```haskell data Person = Person { name :: String, father :: Kanskje Person, mother :: Kanskje Person } deriving (Show, Eq) ``` ```haskell grandfathers :: Person -> Kanskje (Person, Person) ``` Eksempel på bruk: ```html *Main> grandfathers teodor Bare (Leif,Alf) *Main> grandfathers tore Tull *Main> grandfathers hanne Tull ``` --- ## Bestefedre, java ```java public class Person { public String name; public Person father; public Person mother; public Person[] grandFathers() { if (father != null) { if (father.father != null) { if (mother != null) { if (mother.mother != null) { return new Person[]{father.father, mother.mother}; } } } } return null; } } ``` (Java har ikke noen tuppel-type, så vi bruker en ``Array``.) --- ## Bestefedre, java-aktig haskell ```haskell fathersfather :: Person -> Kanskje Person fathersfather person = case father person of Bare f -> father f otherwise -> Tull mothersfather :: Person -> Kanskje Person mothersfather person = case mother person of Bare m -> father m otherwise -> Tull bestefedre person = case fathersfather person of Bare ff -> case mothersfather person of Bare mf -> Bare (ff, mf) otherwise -> Tull otherwise -> Tull ``` Grusomt. Får ikke engang plass på sliden. Hint om at vi gjør noe dårlig. .footnote[Her bruker vi ``case``-setninger. De trenger vi ikke videre, så vi bruker ikke tid på det.] --- ## Infix-funksjoner - ``add 2 3`` gir ``5`` - ``2 `add` 3`` gir ``5`` Vi kan sette backticks (`` ` ` ``) rundt funksjoner for å bruke funksjoner *mellom* argumentene. På et norsk tastatur får man en backtick ved å trykke ``Shift+\`` to ganger. --- ## Bestefedreproblematikk Styggheten oppstår når vi vi skal putte inn ``Kanskje Person`` i en funksjon av typen ``Person -> Kanskje Person``. Det lager vi en funksjon til! ```haskell bindKanskjePerson :: Kanskje Person -> (Person->Kanskje Person) -> Kanskje Person bindKanskjePerson Tull _ = Tull bindKanskjePerson (Bare p) f = f p ``` -- Hmm. Vi bruker jo ingen egenskaper fra Person. Kan gjøre det for alle typer! -- ```haskell bindKanskje :: Kanskje a -> (a -> Kanskje b) -> Kanskje b bindKanskje (Bare x) f = f x bindKanskje Tull _ = Tull ``` -- Hva med å samle flere kanskje-verdier i en tuppel? ```haskell combineKanskje :: Kanskje a -> Kanskje b -> Kanskje (a, b) combineKanskje (Bare x) (Bare y) = Bare (x, y) combineKanskje _ _ = Tull ``` --- ## Besteforeldre sånn de skal være ```haskell fathersfather' :: Person -> Kanskje Person fathersfather' p = bindKanskje (father p) father mothersfather' :: Person -> Kanskje Person mothersfather' p = bindKanskje (mother p) mother grandfathers' p = combineKanskje (fathersfather' p) (mothersfather' p) ``` --- ## Kanskje en monade? ``Monad`` er en typeklasse: ```haskell class Monad m where (>>=) :: m a -> (a -> m b) -> m b -- >>= uttales "bind" return :: a -> m a ``` Hmm, ``(>>=)`` virker litt kjent. -- Hva om ``m`` er ``Kanskje``? -- ```haskell (>>=) :: Kanskje a -> (a -> Kanskje b) -> Kanskje b ``` -- Næmmen! ``(>>=)`` har vi jo allerede laget! ```haskell bindKanskje :: Kanskje a -> (a -> Kanskje b) -> Kanskje b ``` --- ## Kanskje en monade? (2) ``Monad`` er en typeklasse: ```haskell class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a ``` ``return`` pakker inn en verdi. -- Setter ``m`` til ``Kanskje``. ```haskell return :: a -> Kanskje a ``` Hmm. Typen til ``father`` stemmer, men vi skal da pakke inn personen, ikke faren. -- Hva med ``Bare``? ```html Main> :t Bare Bare :: a -> Kanskje a ``` --- ## Kanskje er en monade! (Etter vi har sagt hvordan) ```haskell class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a ``` ```haskell instance Monad Kanskje where (>>=) = bindKanskje return = Bare ``` -- ```html Main> (Bare teodor) >>= father Bare Tore Main> (Bare teodor) >>= father >>= father Bare Leif Main> (Bare teodor) >>= father >>= father >>= father Tull ``` ```html Main> (Bare teodor) >>= mother Bare Hanne Main> (Bare teodor) >>= mother >>= mother Tull Main> (Bare teodor) >>= mother >>= father Bare Alf ``` --- ## Maybe Kanskje kan være nyttig. Og den finnes allerede! ```html Main> :info Maybe data Maybe a = Nothing | Just a -- Defined in `Data.Maybe' instance Eq a => Eq (Maybe a) -- Defined in `Data.Maybe' instance Monad Maybe -- Defined in `Data.Maybe' instance Functor Maybe -- Defined in `Data.Maybe' instance Ord a => Ord (Maybe a) -- Defined in `Data.Maybe' instance Read a => Read (Maybe a) -- Defined in `GHC.Read' instance Show a => Show (Maybe a) -- Defined in `GHC.Show' ``` --- template: inverse name: io # IO Skitten på overflaten, silkemyk inni. --- ## Hello, World ```haskell main = putStrLn "Ohai. This is from Haskell!" ``` Kan nå kjøre programmet i kommandolinja ```html > runhaskell hello.hs Ohai. This is from Haskell! ``` --- ## Ren IO IO kan gjøre hva som helst. Hva kan da typen til IO-funksjoner være? -- IO har tilgang til verden sånn som den er, og endrer verden til noe annet. -- ```haskell main :: World -> World ``` .footnote[Grovt forenkla. GHC godtar ikke dette.] -- Vi må komplisere dette *litt* så vi får med alt vi trenger. ```haskell main :: World -> ((), World) ``` ``()`` er den tomme typen. --- ## IO-funksjoner For å gjøre noe nyttig, trenger vi input fra brukeren: ```haskell getLine :: World -> (String, World) ``` ... der får vi bruk for den ekstra "plassen" vår! -- Hva med typen til ``putStrLn``, som skriver ut tekst på ei ny linje? -- Den er også en verdenstransformasjon når den har fått input! ```haskell putStrLn "Hello, there!" :: World -> ((), World) ``` -- ```haskell putStrLn :: String -> ( World -> ((), World) ) ``` ```haskell putStrLn :: String -> World -> ((), World) ``` --- ## Hello, World med ny syntaks Når main skal ha signaturen ```haskell main :: World -> World ``` får vi ```haskell main w0 = let (_, w1) = putStrLn "Hello!" w0 in w1 ``` --- ## ... så legger vi på litt ```haskell main w0 = let (_ , w1) = putStr "Please enter your name: " in let (name, w2) = getLine w1 in let (_ , w3) = putStrLn ("Hello, " ++ name) w2 in w2 ``` --- ## Alle let-ene her ble stygt. Kanskje monader kan redde dagen? Vi prøver. ```haskell bindIO :: (World -> (a,World)) -> (a -> (World -> (b,World))) -> (World -> (b,World)) ``` -- Typene for ``getLine`` og ``putStrLn`` (og ``putStr``) har vi: ```haskell getLine :: World -> (String, World) putStrLn :: String -> World -> ((), World) ``` -- ... og da kan vi skrive et program som kommuniserer med verden! ```haskell main = putStr "Please enter your name: " >>= \trash -> -- putStr returnerer ingenting nyttig, getLine >>= \name -> -- ... men navnet må vi ha. putStrLn ("Hello, " ++ name) ``` --- name: do template: inverse # Do-notasjon Monader blir en fryd! --- ## Do it with IO! Do gjør at vi slipper å bruke ``>>=`` for å hekte sammen IO. ```haskell main = getLine >>= \name -> putStrLn ("Hello, " ++ name ++ "!") ``` -- Med do-notasjon kan vi gjøre dette mer lesbart. ``<-`` er bare lov med do-notasjon. ```haskell main = do name <- getLine putStrLn ("Hello, " ++ name ++ "!") ``` -- ``do`` hekter sammen monader. --- ## Do it Kanskje? ```haskell grandfathers'' p = do ff <- father p >>= father mf <- mother p >>= father return (ff, mf) ``` -- ``<-`` gir oss en ren type. For ``Kanskje Person`` er dette en ``Person``. La oss brekke den! ```haskell grandfathers'' p = do ff <- father p >>= father mf <- mother p >>= father _ <- Tull return (ff, mf) ``` --- ## Do make dem lists ```haskell import Control.Monad (guard) pythagoreans = do z <- [1..] y <- [1..z] x <- [1..y] guard (x*x + y*y == z*z) return (x, y, z) ``` Guard brekker monaden når den får inn ``True``, og lar den fortsette om den får inn ``False``. --- name: last-page template: inverse ## Det var alt! Takk til Erik og Tor for input underveis. Slides ved hjelp av [remark](http://github.com/gnab/remark).