Moderne Funksjonell Programmering i Lisp Lars Tveito 11. mai, 2017 Institutt for Informatikk, University of Oslo
Introduksjon
Abstract Vi skal utforske programmeringsspråket Clojure, en moderne Lisp-dialekt. Språket ønsker å oppnå bekymringsfri håndtering av samtidighet (eng: concurrency), et svært sentralt problem i moderne programvareutvikling. Clojure favoriserer ikke-muterbar data og rene funksjoner. Vi skal se hvordan Clojure utvider tradisjonelle Lisp-dialekter med ikke-muterbare implementasjoner av lister, vektorer og hashmaps som samles under begrepet persistente datastrukturer. Videre skal vi se hvordan Clojure utnytter tre referansetyper med ulik semantikk for samtidighet sammen med persistente datastrukturer for å oppnå gode og pragmatiske løsninger for samtidighet. Til slutt skal vi se at Clojure er et språk for hipstere med kule briller og GitHub-logo som dekker det halvspiste eplet. Clojurescript er Clojure som kompileres til Javascript og kan dermed brukes til utvikling av web-apps, samt bygge native mobilapplikasjoner for Android og ios! Dette er ikke pensum for INF2810. Det er allikevel fryktelig interessant. 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng Clojure er bygget på Java Alt fra Java kan kalles på 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng Clojure er bygget på Java Alt fra Java kan kalles på Clojure er (i likhet med Scheme) dynamisk typet 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng Clojure er bygget på Java Alt fra Java kan kalles på Clojure er (i likhet med Scheme) dynamisk typet Clojure favoriserer rene funksjoner på ikke-muterbar data 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng Clojure er bygget på Java Alt fra Java kan kalles på Clojure er (i likhet med Scheme) dynamisk typet Clojure favoriserer rene funksjoner på ikke-muterbar data Clojure forsøker å løse problemer mht. samtidighet (eng: concurrency) 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng Clojure er bygget på Java Alt fra Java kan kalles på Clojure er (i likhet med Scheme) dynamisk typet Clojure favoriserer rene funksjoner på ikke-muterbar data Clojure forsøker å løse problemer mht. samtidighet (eng: concurrency) Clojure er laget av Rich Hickey en gud blant mennesker 1
Litt om Clojure Clojure dukket for 10 år siden og lansert i 2009 Dette er ganske nytt i programmeringsspråksammenheng Clojure er bygget på Java Alt fra Java kan kalles på Clojure er (i likhet med Scheme) dynamisk typet Clojure favoriserer rene funksjoner på ikke-muterbar data Clojure forsøker å løse problemer mht. samtidighet (eng: concurrency) Clojure er laget av Rich Hickey en gud blant mennesker 1
Men først, litt kode fra oblig1a 2
Men først, litt kode fra oblig1a Oppgave 3 (a) (defn add1 [n] (+ n 1)) Oppgave 3 (b) (defn plus [n m] (if (zero? m) n (add1 (plus n (sub1 m))))) (defn sub1 [n] (- n 1)) Oppgave 3 (c) (defn plus [n m] (if (zero? m) n (recur (add1 n) (sub1 m)))) 2
Men først, litt kode fra oblig1a (forts.) Oppgave 3 (e) (defn fib ([n] (fib 1 0 n)) ([a b count] (if (zero? count) b (recur (+' a b) a (dec count))))) 3
Men først, litt kode fra oblig1a (forts.) Oppgave 3 (e) i Clojure-stil (defn fib [n] (let [fib-step (fn [[a b]] [b (+' a b)]) fibs (iterate fib-step [1 1])] (first (nth fibs n)))) 4
Men først, litt kode fra oblig1a (forts.) Oppgave 3 (e) i Clojure-stil (defn fib [n] (let [fib-step (fn [[a b]] [b (+' a b)]) fibs (iterate fib-step [1 1])] (first (nth fibs n)))) Enda mer Clojure-stil (defn fib [n] ( (fn [[a b]] [b (+' a b)]) (iterate [1 1]) (nth n) first)) 4
Persistente datastrukturer
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs (men ikke hvis du bruker set-car! eller set-cdr!!) 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs (men ikke hvis du bruker set-car! eller set-cdr!!) Clojure har ikke-muterbare og persistente implementasjoner av 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs (men ikke hvis du bruker set-car! eller set-cdr!!) Clojure har ikke-muterbare og persistente implementasjoner av Lister (1 2 3) 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs (men ikke hvis du bruker set-car! eller set-cdr!!) Clojure har ikke-muterbare og persistente implementasjoner av Lister Vektorer (1 2 3) [1 2 3] 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs (men ikke hvis du bruker set-car! eller set-cdr!!) Clojure har ikke-muterbare og persistente implementasjoner av Lister Vektorer Mengder (1 2 3) [1 2 3] #{1 2 3} 5
Persistente Datastrukturer En datastruktur er persistent hvis den ikke ødelegger gamle versioner av seg selv Scheme sine lister er persistente fordi et kall på (cons x xs) ikke påvirker xs (men ikke hvis du bruker set-car! eller set-cdr!!) Clojure har ikke-muterbare og persistente implementasjoner av Lister (1 2 3) Vektorer [1 2 3] Mengder #{1 2 3} Hashmaps {:a 1, :b 2, :c 3} 5
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] (conj #{1 2 3} 4) => #{1 4 3 2} 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] (conj #{1 2 3} 4) => #{1 4 3 2} (conj {:a 1 :b 2 :c 3} [:d 4]) => {:a 1, :b 2, :c 3, :d 4} 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] (conj #{1 2 3} 4) => #{1 4 3 2} (conj {:a 1 :b 2 :c 3} [:d 4]) => {:a 1, :b 2, :c 3, :d 4} count hvor mange ting er i samlingen? 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] (conj #{1 2 3} 4) => #{1 4 3 2} (conj {:a 1 :b 2 :c 3} [:d 4]) => {:a 1, :b 2, :c 3, :d 4} count hvor mange ting er i samlingen? De kan brukes som sekvenser 1 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] (conj #{1 2 3} 4) => #{1 4 3 2} (conj {:a 1 :b 2 :c 3} [:d 4]) => {:a 1, :b 2, :c 3, :d 4} count hvor mange ting er i samlingen? De kan brukes som sekvenser 1 map, filter, reduce og mange flere 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Alle datastrukturer i Clojure støtter conj en mer generell cons (conj '(1 2 3) 4) => (4 1 2 3) (conj [1 2 3] 4) => [1 2 3 4] (conj #{1 2 3} 4) => #{1 4 3 2} (conj {:a 1 :b 2 :c 3} [:d 4]) => {:a 1, :b 2, :c 3, :d 4} count hvor mange ting er i samlingen? De kan brukes som sekvenser 1 map, filter, reduce og mange flere For hashmaps får vi en liste på formen ([:a 1] [:b 2] ) 1 De implementerer ISeq interfacet, som kan minne om Itrable fra Java. 6
Persistente Datastrukturer (forts.) Persistente datastrukturer baserer seg på strukturell deling 2 xs d b g a c f h 2 By The original uploader was VineetKumar at English Wikipedia - Transferred from en.wikipedia to Commons by sevela.p., CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3594619 7
Persistente Datastrukturer (forts.) Persistente datastrukturer baserer seg på strukturell deling 2 xs xs ys d b d g d g b g a c f f h a c f h e 2 By The original uploader was VineetKumar at English Wikipedia - Transferred from en.wikipedia to Commons by sevela.p., CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3594619 7
Persistente Datastrukturer (forts.) Lister O(1) for endring i begynnelsen (conj '(1 2 3) 4) O(n) for oppslag (nth '(1 2 3 4) 2) Egner seg når man skal traversere hele 8
Persistente Datastrukturer (forts.) Lister O(1) for endring i begynnelsen (conj '(1 2 3) 4) O(n) for oppslag (nth '(1 2 3 4) 2) Egner seg når man skal traversere hele Vektorer O(1) for innsetting på slutten (conj [1 2 3] 4) O(1) for oppslag ([1 2 3 4] 2) O(1) for (ikke destruktiv) endring (assoc [1 2 3 3] 3 4) Egner seg når man bryr seg om posisjon trenger raskt oppslag 8
Persistente Datastrukturer (forts.) Mengder O(1) for innsetting (conj #{1 2 3} 4) O(1) for sjekk av medlemskap (#{1 2 3} 3) Egner seg når du bryr deg om noe er i mengden eller ikke 9
Persistente Datastrukturer (forts.) Mengder O(1) for innsetting (conj #{1 2 3} 4) O(1) for sjekk av medlemskap (#{1 2 3} 3) Egner seg når du bryr deg om noe er i mengden eller ikke Hashmaps O(1) for innsetting (assoc {:a 1} :b 2) O(1) for oppslag ({:a 1, :b 2} :b) O(1) for (ikke destruktiv) endring (dissoc {:a 1 :b 2} :b) Egner seg når du trenger en assosiativ datastruktur 9
INF1010 Oblig i Clojure
Legg til Sortert (ns inf1010-oblig4.core (:require [clojure.core.reducers :as r])) (defn split [xs x] (split-with (comp neg? #(compare % x)) xs)) (defn add-sorted [xs x] (let [[smaller larger] (split xs x)] (reduce into [] [smaller [x] larger]))) 10
Fletting (defn merge-sorted ([] []) ([xs ys] (merge-sorted xs ys [])) ([xs ys res] (let [x (first xs) y (first ys)] (cond (empty? xs) (into res ys) (empty? ys) (into res xs) (neg? (compare x y)) (recur (rest xs) ys (conj res x)) :else (recur xs (rest ys) (conj res y)))))) 11
Sortering Sekvensiell sortering (defn single-threaded-sort [xs] (reduce add-sorted [] xs)) 12
Sortering Sekvensiell sortering (defn single-threaded-sort [xs] (reduce add-sorted [] xs)) Parallellisert sortering (defn threaded-sort [xs n] (let [chunk-size ( xs count (quot n) inc)] (r/fold chunk-size merge-sorted add-sorted xs))) 12
Tilstandsendring i Clojure
Verdier, Identitet og Tilstand En verdi er noe som ikke endrer seg 13
Verdier, Identitet og Tilstand En verdi er noe som ikke endrer seg Tallet 2 og 11. mai er verdier, men et Java-objekt er ikke det 13
Verdier, Identitet og Tilstand En verdi er noe som ikke endrer seg Tallet 2 og 11. mai er verdier, men et Java-objekt er ikke det Clojure skiller mellom identitet og tilstand 13
Verdier, Identitet og Tilstand En verdi er noe som ikke endrer seg Tallet 2 og 11. mai er verdier, men et Java-objekt er ikke det Clojure skiller mellom identitet og tilstand En identitet er en stabil logisk enhet som har en serie av verdier over tid 13
Verdier, Identitet og Tilstand En verdi er noe som ikke endrer seg Tallet 2 og 11. mai er verdier, men et Java-objekt er ikke det Clojure skiller mellom identitet og tilstand En identitet er en stabil logisk enhet som har en serie av verdier over tid En tilstand er en verdi ved et gitt tidspunkt 13
Referansetyper Trådlokale variabler kan endres med set!, tilsvarende Scheme 14
Referansetyper Trådlokale variabler kan endres med set!, tilsvarende Scheme Lite idiomatisk og sjeldent brukt 14
Referansetyper Trådlokale variabler kan endres med set!, tilsvarende Scheme Lite idiomatisk og sjeldent brukt Vi har tre forskjellige referansetyper, som har andledes semantikk mht. concurrency 14
Referansetyper Trådlokale variabler kan endres med set!, tilsvarende Scheme Lite idiomatisk og sjeldent brukt Vi har tre forskjellige referansetyper, som har andledes semantikk mht. concurrency Refs synkron og koordinert tilstandsendring 14
Referansetyper Trådlokale variabler kan endres med set!, tilsvarende Scheme Lite idiomatisk og sjeldent brukt Vi har tre forskjellige referansetyper, som har andledes semantikk mht. concurrency Refs synkron og koordinert tilstandsendring Atoms synkron og ukoordinert tilstandsendring 14
Referansetyper Trådlokale variabler kan endres med set!, tilsvarende Scheme Lite idiomatisk og sjeldent brukt Vi har tre forskjellige referansetyper, som har andledes semantikk mht. concurrency Refs synkron og koordinert tilstandsendring Atoms synkron og ukoordinert tilstandsendring Agents asynkron og ukoordinert tilstandsendring 14