• Keine Ergebnisse gefunden

Warum Reaktive Programmierung?

N/A
N/A
Protected

Academic year: 2022

Aktie "Warum Reaktive Programmierung?"

Copied!
56
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Reaktive Programmierung

Vorlesung 1 vom 14.04.15: Was ist Reaktive Programmierung?

Christoph Lüth & Martin Ring

Universität Bremen Sommersemester 2014

16:09:52 2015-04-21 1 [36]

Organisatorisches

I Vorlesung: Donnerstags 8-10, MZH 1450

I Übung: Dienstags 16-18, MZH 1460 (nach Bedarf)

I Webseite:www.informatik.uni-bremen.de/~cxl/lehre/rp.ss15

I Scheinkriterien:

IVoraussichtlich 6 Übungsblätter

IAlle bearbeitet, insgesamt 40% (Notenspiegel PI3)

IÜbungsgruppen 2 – 4 Mitglieder

IFachgespräch am Ende

2 [36]

Warum Reaktive Programmierung?

Herkömmliche Programmiersprachen:

IC, C++

IJavaScript, Ruby, PHP, Python IJava

I(Haskell) Eigenschaften:

IImperativundprozedural ISequentiell

Zugrundeliegendes Paradigma:

. . . aber die Welt ändert sich:

I DasNetzverbindet Rechner I Selbst eingebettete Systeme sind

vernetzt (Auto: ca. 100 Proz.) I Mikroprozessoren sindmehrkernig I Systeme sindeingebettet,

nebenläufig,reagierenauf ihre Umwelt.

3 [36]

Probleme mit dem herkömmlichen Ansatz

Programm

Daten Eingabe

Ausgabe

Daten Programm

Eingabe Ausgabe

Daten Programm

Eingabe Ausgabe

Eingabe

Ausgabe Programm Daten

Eingabe Ausgabe

Eingabe Ausgabe

Eingabe

Ausgabe

Daten Programm

Eingabe Ausgabe

Eingabe Ausgabe

Eingabe

Ausgabe Eingabe Ausgabe

Daten Programm

Eingabe Ausgabe

Eingabe Ausgabe

Eingabe Ausgabe

Eingabe

Ausgabe Eingabe Ausgabe

IProblem:Nebenläufigkeit INebenläufigkeit verursacht

Synchronisationsprobleme IBehandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte:

Locks, Semaphoren, Monitore

4 [36]

Amdahl’s Law

“The speedup of a program using multiple processors in parallel computing is limited by the sequential fraction of the program. For example, if 95% of the program can be parallelized, the theoretical maximum speedup using parallel computing would be 20×as shown in the diagram, no matter how many processors are used.”

20.00 18.00 16.00 14.00 12.00 10.00 8.00 6.00 4.00 2.00 0.00

Speedup 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536

NumberLofLProcessors Amdahl’sLLaw

ParallelLPortion 50%

75%

90%

95%

Quelle: Wikipedia

5 [36]

The Reactive Manifesto

I http://www.reactivemanifesto.org/

Responsive

Resilient

Message Driven Elastic

6 [36]

Was ist Reaktive Programmierung?

I ImperativeProgrammierung: Zustandsübergang

I ProzeduralundOO: Verkapselter Zustand

I FunktionaleProgrammierung: Abbildung (mathematische Funktion)

I ReaktiveProgrammierung:

1.Datenabhängigkeit

2.Reaktiv=funktional+nebenläufig

7 [36]

Datenflusssprachen (data flow languages)

I Frühe Sprachen: VAL, SISAL, ID, LUCID (1980/1990)

I Heutige Sprachen: Esterel, Lustre (Gérard Berry, Verimag)

IKeineZuweisungen, sondernDatenfluss

ISynchron:alle Aktionen ohne Zeitverzug

IVerwendung in der Luftfahrtindustrie (Airbus)

8 [36]

(2)

Fahrplan

I Teil I: Grundlegende Konzepte

I Was ist Reaktive Programmierung?

I Nebenläufigkeit und Monaden in Haskell

I Funktional-Reaktive Programmierung

I Einführung in Scala

I Die Scala Collections

I ScalaCheck

I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

9 [36]

Fahrplan

I Teil I: Grundlegende Konzepte

I Teil II: Nebenläufigkeit

IFutures and Promises

IReaktive Datenströme I

IReaktive Datenströme II

IDas Aktorenmodell

IAktoren und Akka

I Teil III: Fortgeschrittene Konzepte

10 [36]

Fahrplan

I Teil I: Grundlegende Konzepte

I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

I Bidirektionale Programmierung: Zippers and Lenses

I Robustheit, Entwurfsmuster

I Theorie der Nebenläufigkeit

11 [36]

Rückblick Haskell

I Definition von Funktionen:

Ilokale Definitionen mitletundwhere

IFallunterscheidung undguarded equations

IAbseitsregel

IFunktionen höherer Ordnung

I Typen:

IBasisdatentypen:Int,Integer,Rational,Double,Char,Bool

IStrukturierte Datentypen:[a],(a, b)

IAlgebraische Datentypen:data Maybe a = Just a | Nothing

12 [36]

Rückblick Haskell

I Abstrakte Datentypen

I Module

I Typklassen

I Verzögerte Auswertung und unendliche Datentypen

13 [36]

Ein- und Ausgabe in Haskell

Umwelt Haskell

Aktionen

Umwelt Reine

Funktionen

Haskell

Problem:

IFunktionen mit Seiteneffekten nicht referentiell transparent.

I readString :: . . .→String??

Lösung:

ISeiteneffekte am Typ erkennbar IAktionenkönnennurmit

Aktionenkomponiert werden I„einmal Aktion, immer Aktion“

14 [36]

Aktionen als abstrakter Datentyp

I ADT mit OperationenKompositionundLifting

I Signatur:

typeIOα

(=) :: IOα→ (α→IOβ) →IOβ return :: α→ IOα

I PluselementareOperationen (lesen, schreiben etc)

15 [36]

Elementare Aktionen

I Zeile vonstdinlesen:

getLine :: IO String

I Zeichenkette aufstdoutausgeben:

putStr :: String→ IO ()

I Zeichenkette mit Zeilenvorschubausgeben:

putStrLn :: String→ IO ()

16 [36]

(3)

Einfache Beispiele

I Echoeinfach echo1 :: IO ()

echo1 = getLine= putStrLn I Echomehrfach

echo :: IO ()

echo = getLine= putStrLn=λ_→ echo I Was passiert hier?

I Verknüpfenvon Aktionen mit=

I Jede Aktion gibtWertzurück

17 [36]

Noch ein Beispiel

I Umgekehrtes Echo:

ohce :: IO () ohce = getLine

=λs→ putStrLn ( reverse s ) ohce

I Was passiert hier?

IReineFunktionreversewird innerhalb vonAktionputStrLngenutzt

IFolgeaktionohcebenötigtWertder vorherigen Aktion nicht

IAbkürzung:

pq = p=λ_→ q

18 [36]

Die do-Notation

I Syntaktischer Zucker fürIO:

echo = getLine

=λs→ putStrLn s echo

⇐⇒

echo =

do s← getLine putStrLn s echo

I Rechts sind=,implizit.

I Es gilt dieAbseitsregel.

I Einrückungderersten Anweisungnachdobestimmt Abseits.

19 [36]

Drittes Beispiel

I Zählendes, endliches Echo echo3 :: Int→ IO () echo3 cnt =do

putStr (show cnt ++ " : ␣" ) s← getLine

i f s6= "" then do

putStrLn $ show cnt ++ " : ␣"++ s echo3 ( cnt+ 1)

else return () I Was passiert hier?

IKombinationausKontrollstrukturenundAktionen

IAktionenalsWerte

IGeschachteltedo-Notation

20 [36]

Module in der Standardbücherei

I Ein/Ausgabe, Fehlerbehandlung (ModulIO) I Zufallszahlen (ModulRandom)

I Kommandozeile, Umgebungsvariablen (ModulSystem) I Zugriff auf das Dateisystem (ModulDirectory) I Zeit (ModulTime)

21 [36]

Ein/Ausgabe mit Dateien

I ImPreludevordefiniert:

IDateien schreiben (überschreiben, anhängen):

type FilePath = String

w r i t e F i l e :: FilePath → String →IO () appendFile :: FilePath → String →IO () I Datei lesen (verzögert):

readFile :: FilePath →IO String I Mehr Operationenim ModulIOder Standardbücherei

IBuffered/Unbuffered, Seeking, &c.

IOperationen aufHandle

22 [36]

Beispiel: Zeichen, Wörter, Zeilen zählen (wc)

wc :: String→IO () wc f i l e =

docont ← readFile f i l e putStrLn $ f i l e ++ " : ␣"++

show ( length ( l i n e s cont ) , length (words cont ) , length cont ) I Datei wird gelesen

I Anzahl Zeichen, Worte, Zeilen gezählt

23 [36]

Aktionen als Werte

I AktionensindWertewie alle anderen.

I DadurchDefinitionvonKontrollstrukturenmöglich.

I Endlosschleife:

forever :: IO α→IOα forever a = aforever a I Iteration (feste Anzahl):

forN :: Int→IOα→ IO () forN n a | n == 0 = return ()

| otherwise = aforN (n−1) a I VordefinierteKontrollstrukturen (Control .Monad):

Iwhen,mapM,forM,sequence, . . .

24 [36]

(4)

Map und Filter für Aktionen

I Listen von Aktionen sequenzieren:

sequence :: [ IO a ]→ IO [ a ] sequence_ :: [ IO ( ) ]→IO () I Map für Aktionen:

mapM :: (a→IO b)→ [ a ]→IO [ b ] mapM_ :: (a→ IO ())→ [ a ]→IO () I Filter für Aktionen

I Importieren mitimport Monad (filterM).

filterM :: (a →IO Bool) → [ a ] →IO [ a ]

25 [36]

Fehlerbehandlung

I Fehlerwerden durchExceptionrepräsentiert

IExceptionistTypklasse— kann durch eigene Instanzen erweitert werden

IVordefinierte Instanzen: u.a.IOError

I FehlerbehandlungdurchAusnahmen(ähnlich Java) catch :: Exception e⇒ IOα→ (e→ IOα) →IOα try :: Exception e⇒ IOα→IO ( Either e a)

I Faustregel:catchfür unerwartete Ausnahmen,tryfür erwartete I Fehlerbehandlungnur in Aktionen

26 [36]

Fehler fangen und behandeln

I Fehlerbehandlung fürwc:

wc2 :: String→ IO () wc2 f i l e =

catch (wc f i l e )

(λe → putStrLn $ " Fehler : ␣"++ show (e :: IOException ))

I IOErrorkann analysiert werden (sieheSystem.IO.Error) I readmit Ausnahme bei Fehler (statt Programmabbruch):

readIO :: Read a⇒ String→IO a

27 [36]

Ausführbare Programme

I Eigenständiges Programm istAktion

I Hauptaktion:main :: IO ()in ModulMain I wcals eigenständiges Programm:

moduleMainwhere

importSystem . Environment ( getArgs ) importControl . Exception

. . .

main :: IO () main =do

args ← getArgs mapM_ wc2 args

28 [36]

So ein Zufall!

I Zufallswerte:

randomRIO :: (α,α)→ IOα

I Warum istrandomIOAktion?

I Beispiele:

I Aktion zufällig oft ausführen:

atmost :: Int→IOα→IO [α]

atmost most a =

dol←randomRIO (1 , most) sequence ( r e p l i c a t e l a)

I Zufälligen String erzeugen:

randomStr :: IO String

randomStr = atmost 40 (randomRIO ( ’ a ’ , ’ z ’ ) )

29 [36]

Funktionen mit Zustand

I Idee: Seiteneffektexplizitmachen

I Funktionf:ABmit Seiteneffekt inZustandS:

f :A×SB×S

∼=

f :ASB×S

I Datentyp:SB×S

I Komposition: Funktionskomposition unduncurry

30 [36]

In Haskell: Zustände explizit

I Datentyp: Berechnung mit Seiteneffekt in Typσ(polymorph überα) type State σ α=σ→ (α, σ)

I Komposition zweier solcher Berechnungen:

comp :: Stateσ α→ (α→ Stateσ β)→ State σ β comp f g = uncurry g◦f

I Lifting:

l i f t :: α→ State σ α l i f t = curry id

31 [36]

Beispiel: Ein Zähler

I Datentyp:

typeWithCounterα= State Int α I Zähler erhöhen:

t i c k :: WithCounter () t i c k i = ( ( ) , i+1) I Zähler auslesen:

read :: WithCounter Int read i = ( i , i ) I Zähler zurücksetzen:

reset :: WithCounter () reset i = ( ( ) , 0)

32 [36]

(5)

Implizite vs. explizite Zustände

I Nachteil: Zustand istexplizit

I Kanndupliziertwerden

I Daher: Zustandimplizitmachen

I Datentypverkapseln

I SignaturState , comp, lift, elementare Operationen I Beispiel für eineMonade

I Generische Datenstruktur, dieVerkettungvonBerechnungenerlaubt

33 [36]

Aktionen als Zustandstransformationen

I Idee: Aktionen sindTransformationenauf SystemzustandS

I Sbeinhaltet

ISpeicher als AbbildungA*V(AdressenA, WerteV)

IZustand des Dateisystems

IZustand des Zufallsgenerators

I In Haskell: TypRealWorld

typeIOα= State RealWorldα −−... oder so ähnlich

I“Virtueller” Typ, Zugriff nur über elementare Operationen

IEntscheidend nurReihenfolgeder Aktionen

34 [36]

War das jetzt reaktiv?

I Haskell istfunktional

I Für eine reaktive Sprache fehltNebenläufigkeit

I Nächste Vorlesung: Concurrent Haskell

I Damit könnten wir die Konzepte dieser VL modellieren

I Besser:Scala = Funktional + JVM

35 [36]

Zusammenfassung

I Reaktive Programmierung: Beschreibung derAbhängigkeitvon Daten

I Rückblick Haskell:

IAbhängigkeit von Aussenwelt in TypIOkenntlich

IBenutzung von IO: vordefinierte Funktionen in der Haskell98 Bücherei

IWerte vom TypIO(Aktionen) können kombiniert werden wie alle anderen I Nächstes Mal:

IMonaden und Nebenläufigkeit in Haskell

36 [36]

(6)

Reaktive Programmierung

Vorlesung 2 vom 16.04.15: Monaden und Nebenlaeufigkeit in Haskell

Christoph Lüth & Martin Ring Universität Bremen Sommersemester 2015

16:11:07 2015-05-26 1 [32]

Fahrplan

I Teil I: Grundlegende Konzepte

IWas ist Reaktive Programmierung?

INebenläufigkeit und Monaden in Haskell

IFunktional-Reaktive Programmierung

IEinführung in Scala

IDie Scala Collections

IScalaTest und ScalaCheck I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

2 [32]

Speisekarte

I Das Geheimnis der Monade

I Concurrent Haskell

3 [32]

Zustandsübergangsmonaden

I Aktionen (IO a) sind keine schwarze Magie.

I Grundprinzip: Systemzustand Σ wird explizit behandelt.

f::aIO b ∼= f :: (a,Σ)→(b,Σ) FolgendeInvariantenmüssen gelten:

ISystemzustand darfnie dupliziertodervergessenwerden.

IAuswertungsreihenfolge muss erhalten bleiben.

I KompositionmussInvariantenerhalten Zustandsübergangsmonaden

4 [32]

Komposition von Zustandsübergängen

I Im Prinzip Vorwärtskomposition:

(=) :: ST s a→ (a→ST s b)→ST s b

(=) :: ( s→ (a , s ))→ (a→ s→ (b , s ))→ ( s→ (b , s )) (=) :: ( s→ (a , s ))→ ((a , s )→ (b , s ))→ ( s→ (b , s ))

I Damitf =g =uncurry g◦f I Aber:STkannkein Typsynonymsein

I Nötig:abstrakter DatentypumInvariantenzu erhalten

5 [32]

ST als Abstrakter Datentyp

I Datentyp verkapseln:

newtypeST s a = ST ( s→ (a , s ))

I Hilfsfunktion (Selektor)

unwrap :: ST s a→ ( s→ (a , s )) unwrap (ST f ) = f

I Damit ergibt sich

f= g = ST ( uncurry (unwrap . g)◦unwrap f ) return a = ST (λs→ (a , s ))

6 [32]

Aktionen

I Aktionen: Zustandstransformationen auf der Welt I TypRealWorld#repräsentiert Außenwelt

I Typ hat genau einen Wertrealworld #, der nur für initialen Aufruf erzeugt wird.

I Aktionen:typeIO a = ST RealWorld# a I Optimierungen:

I ST s adurchin-place-updateimplementieren.

I IO-Aktionen durcheinfachen Aufrufersetzen.

I Compiler darf keine Redexe duplizieren!

I TypIOstelltlediglichReihenfolge sicher.

7 [32]

Was ist eigentlich eine Monade?

I STmodelliertimperative Konzepte.

I Beobachtung:Andere Konzepte könnenähnlich modelliertwerden:

IAusnahmen:f :: a-> Maybe bmit Komposition (=) :: Maybe a→(a→Maybe b)→Maybe b Just a = f = f a

Nothing= f = Nothing

8 [32]

(7)

Monads: The Inside Story

classMonad mwhere

(=) :: m a → (a →m b) →m b return :: a →m a

() :: m a→m b →m b f a i l :: String →m a pq = p=λ_→q

f a i l s = er ro r s

FolgendeGleichungenmüssen (sollten)gelten:

return a=k = k a m=return = m

m=(λx→k x=h) = (m=k)=h

9 [32]

Beispiel: Speicher und Referenzen

I Signatur:

typeMem a instanceMem Monad I Referenzen sind abstrakt:

typeRef

newRef :: Mem Ref I Speicher liest/schreibtString:

readRef :: Ref →Mem String writeRef :: Ref → String→Mem ()

10 [32]

Implementation der Referenzen

Speicher: Liste von Strings, Referenzen: Index in Liste.

typeMem = ST [ String ] −−Zustand type Ref = Int

newRef = ST (λs→ ( length s , s++ [ "" ] ) ) readRef r = ST (λs→ ( s ! ! r , s )) writeRef r v = ST (λs→ ( ( ) ,

take r s ++ [ v ] ++ drop ( r+1) s )) run :: Mem a→ a

run (ST f )= f s t ( f [ ] )

11 [32]

IORef — Referenzen

I Datentyp der Standardbücherei (GHC) importData . IORef

dataIORef a

newIORef :: a → IO (IORef a) readIORef :: IORef a → IO a writeIORef :: IORef a → a →IO () modifyIORef :: IORef a → (a→ a) → IO ()

atomicModifyIORef :: IORef a→ (a→ (a , b)) →IO b I Implementation: “echte” Referenzen.

12 [32]

Beispiel: Referenzen

fac :: Int→IO Int

fac x =doacc← newIORef 1 loop acc xwhere

loop acc 0 = readIORef acc loop acc n =do t ← readIORef acc

writeIORef acc ( t∗ n) loop acc (n−1)

13 [32]

Die Identitätsmonade

I Die allereinfachste Monade:

type Id a = a

instanceMonad Id where return a = a b= f = f b

14 [32]

Die Listenmonade

I Listen sind Monaden:

instance Monad [ ]where m= f = concatMap f m

return x = [ x ] f a i l s = [ ]

I Intuition:f :: a→[b]Liste der möglichen Resultate

I Reihenfolge der Möglichkeiten relevant?

15 [32]

Fehlermonaden

I Erste Nährung:Maybe

I Maybekennt nurNothing, daher strukturierte Fehler:

data Either a b = Left a | Right b type Error a = Either String a instanceMonad ( Either String ) where

( Right a)= f = f a ( Left l ) = f = Left l return b = Right b I Nachteil: Fester Fehlertyp I Lösung: Typklassen

16 [32]

(8)

Exkurs: Was genau ist eigentliche eine Monade?

I Monade: Konstrukt ausKategorientheorie I Monade∼= (verallgemeinerter) Monoid I Monade: gegeben durchalgebraische Theorien

I Operationen endlicher (beschränkter) Aritität

I Gleichungen

I Beispiele:Maybe,List,Set,State, . . . I Monaden in Haskell:computational monads

I Strukturierte Notation fürBerechnungsparadigmen

I Beispiel: Rechner mit Fehler, Nichtdeterminismus, Zustand, . . .

17 [32]

Konzepte der Nebenläufigkeit

I Thread (lightweight process) vs. Prozess Programmiersprache/Betriebssystem Betriebssystem (z.B. Java, Haskell, Linux)

gemeinsamerSpeicher getrennterSpeicher

Erzeugungbillig Erzeugungteuer

mehrereproProgramm einerproProgramm

I Multitasking:

Ipräemptiv:Kontextwechsel wirderzwungen

Ikooperativ:Kontextwechsel nurfreiwillig

18 [32]

Zur Erinnerung: Threads in Java

I Erweiterung der KlassenThreadoderRunnable I Gestartet wird Methoderun()— durch eigene überladen I Starten des Threads durch Aufruf der Methodestart() I Kontextwechsel mityield()

I Je nach JVM kooperativoderpräemptiv.

I Synchronisation mitsynchronize

19 [32]

Threads in Haskell: Concurrent Haskell

I SequentiellesHaskell: Reduktion eines Ausdrucks

IAuswertung

I NebenläufigesHaskell: Reduktion eines Ausdrucks anmehreren Stellen I ghcimplementiert Haskell-Threads

I ModulControl.Concurrententhält Basisfunktionen I Wenige Basisprimitive, darauf aufbauend Abstraktionen

20 [32]

Wesentliche Typen und Funktionen

I Jeder Thread hat einen Identifier: abstrakter TypThreadId I Neuen Thread erzeugen:forkIO :: IO()→IO ThreadId I Thread stoppen: killThread :: ThreadId→IO () I Kontextwechsel: yield :: IO ()

I Eigener Thread:myThreadId :: IO ThreadId I Warten:threadDelay :: Int →IO ()

21 [32]

Rahmenbedingungen

I Zeitscheiben:

ITick: Default 20ms

IContextswitchpro Tick bei Heapallokation

IÄnderungen perKommandozeilenoptionen:+RTS -V<time> -C<time>

I Blockierung:

ISystemaufrufe blockierenalle Threads

IMit threaded library (-threaded) nicht alle

IAber: Haskell Standard-IO blockiertnur den aufrufenden Thread

22 [32]

Concurrent Haskell — erste Schritte

I Ein einfaches Beispiel:

write :: Char→ IO () write c = putChar cwrite c main :: IO ()

main = forkIO ( write ’X’ )write ’O’

I Ausgabeghc: (X|O)

23 [32]

Synchronisation mit MVars

I Basissynchronisationmechanismusin Concurrent Haskell

IAlles andereabgeleitet

I MVar averänderbareVariable (vgl.IORef a) I Entwederleerodergefülltmit Wert vom Typa I Verhalten beim Lesen und Schreiben

Zustand vorher: leer gefüllt

Lesen blockiert(bis gefüllt) danach leer Schreiben danach gefüllt blockiert(bis leer)

INB.Aufweckenblockierter ProzesseeinzelninFIFO

24 [32]

(9)

Basisfunktionen MVars

I Neue Variable erzeugen (leer oder gefüllt):

newEmptyMVar :: IO (MVar a) newMVar :: a → IO (MVar a)

I Lesen:

takeMVar :: MVar a→ IO a

I Schreiben:

putMVar :: MVar a →a → IO ()

25 [32]

Abgeleitete Funktionen MVars

I Nicht-blockierendes Lesen/Schreiben:

tryTakeMVar :: MVar a → IO (Maybe a) tryPutMVar :: MVar a→a→IO Bool

I Änderung der MVar:

swapMVar :: MVar a→ a →IO a

withMVar :: MVar a→ (a →IO b) → IO b modifyMVar :: MVar a→ (a →IO (a , b)) →IO b

IAchtung:race conditions

26 [32]

Ein einfaches Beispiel ohne Synchronisation

I Nebenläufige Eingabe von der Tastatur echo :: String→IO ()

echo p = forever (do

putStrLn ( "∗∗∗␣Please␣enter␣ l i n e ␣fo r␣"++p) l i n e ← getLine

n←randomRIO (1 ,100)

replicateM_ n ( putStr (p++ " : "++ l i n e ++"␣" ))) main :: IO ()

main = forkIO (echo "2")echo "1"

I Problem: gleichzeitige Eingabe I Lösung:MVarsynchronisiert Eingabe

27 [32]

Ein einfaches Beispiel mit Synchronisation

I MVarvoll⇔Eingabe möglich

IAlso: initial voll

I Inhalt der MVar irrelevant:MVar () echo :: MVar ()→ String→IO () echo f l a g p = forever (do

takeMVar f l a g

putStrLn ( "∗∗∗␣Please␣enter␣ l i n e ␣"++ p) l i n e ← getLine

n ←randomRIO (1 ,100)

replicateM_ n ( putStr (p++ " : "++ l i n e ++"␣" )) putMVar f l a g ())

main :: IO ()

main =dof l a g ←newMVar ()

forkIO (echo f l a g "3")forkIO (echo f l a g "2") echo f l a g "1"

28 [32]

Das Standardbeispiel

I Speisende Philosopen

I Philosophi:

I vor dem Esseni-tes und (i+ 1) modn-tes Stäbchen nehmen

I nach dem Essen wieder zurücklegen

I Stäbchen modelliert alsMVar ()

29 [32]

Speisende Philosophen

philo :: [MVar ( ) ] → Int→IO () philo chopsticks i = forever (do

let num_phil = length ( chopsticks )

−−Thinking:

putStrLn ( " Phil␣#"++ show i ++"␣thinks. . ." ) randomRIO (10 , 200)= threadDelay

−−Get ready to eat:

takeMVar ( chopsticks ! ! i )

takeMVar ( chopsticks ! ! (( i+1) ‘mod‘ num_phil))

−−Eat:

putStrLn ( " Phil␣#"++ show i ++"␣eats. . ." ) randomRIO (10 , 200)= threadDelay

−−Done eating:

putMVar ( chopsticks ! ! i ) ()

putMVar ( chopsticks ! ! (( i+1) ‘mod‘ num_phil)) ())

30 [32]

Speisende Philosophen

I Hauptfunktion:nStäbchen erzeugen I Anzahl Philosophen in der Kommandozeile

main =do a :_← getArgs letnum= read a

chopsticks ← replicateM num (newMVar ()) mapM_ ( forkIO◦( philo chopsticks )) [ 0 . . num−1]

block

I Hilfsfunktionblock: blockiert aufrufenden Thread block :: IO ()

block = newEmptyMVar= takeMVar

I NB: Hauptthread terminiert — Programm terminiert!

31 [32]

Zusammenfassung

I Monaden und andere Kuriositäten

IZustandsmonade - Referenzen

IFehlermonaden I Concurrent Haskellbietet

IThreadsauf Quellsprachenebene

ISynchronisierung mitMVars

IDurchschlankes Designeinfache Implementierung I Funktionales Paradigma erlaubtAbstraktionen

IBeispiel:Semaphoren

I Nächste Woche: Funktional-Reaktive Programmierung.

32 [32]

(10)

Reaktive Programmierung

Vorlesung 3 vom 21.04.15: Funktional-Reaktive Programmierung

Christoph Lüth & Martin Ring Universität Bremen Sommersemester 2015

17:10:26 2015-05-19 1 [12]

Fahrplan

I Teil I: Grundlegende Konzepte

IWas ist Reaktive Programmierung?

INebenläufigkeit und Monaden in Haskell

IFunktional-Reaktive Programmierung

IEinführung in Scala

IDie Scala Collections

IScalaTest und ScalaCheck I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

2 [12]

Das Tagemenü

I Funktional-Reaktive Programmierung(FRP) istreinfunktionale, reaktive Programmierung.

I SehrabstraktesKonzept — im Gegensatz zu Observables und Aktoren.

I Literatur: Paul Hudak,The Haskell School of Expression, Cambridge University Press 2000, Kapitel 13, 15, 17.

I Andere (effizientere) Implementierung existieren.

3 [12]

FRP in a Nutshell

I Zwei Basiskonzepte

I Kontinuierliches, über der Zeit veränderlichesVerhalten:

typeTime = Float

typeBehaviour a = Time→ a

I Diskrete Ereignissezu einem bestimmten Zeitpunkt:

typeEvent a = [ (Time, a ) ]

I Obige Typdefinitionen sindSpezifikation, nichtImplementation

4 [12]

Verhalten: erste einfache Beispiele

I Ein kreisender und ein pulsierender Ball:

ci rc , pulse :: Behavior Region

c i r c = t r a n s l a t e ( cos time , s i n time) ( e l l 0.2 0.2) pulse = e l l ( cos time∗ 0.5) ( cos time∗ 0.5)

I Was passiert hier?

I Basisverhalten:time :: Behaviour Time,constB :: a→Behavior a

I Grafikbücherei: DatentypRegion, Funktion Ellipse

I Liftings (∗,0.5,sin, . . . )

5 [12]

Reaktive Animationen: Verhaltensänderung

I Beispiel: auf Knopfdruck Farbe ändern:

color1 :: Behavior Color

color1 = red ‘ untilB ‘ lbp− blue color2r = red ‘ untilB ‘ ce where

ce = ( lbp− blue ‘ untilB ‘ ce ) .|. (key− yellow ‘ untilB ‘ ce ) I Was passiert hier?

I untilBkombiniert Verhalten:

untilB :: Behavior a →Event ( Behavior a) →Behavior a

I=istmapfür Ereignisse:

(=) :: Event a →(a→b) →Event b (− ) :: Event a→b→Event b e− v = e =λ_→v

IKombination von Ereignissen:

6 [12]

Der Springende Ball

ball2 = paint red ( t r a n s l a t e (x , y) ( e l l 0.2 0.2)) whereg = −4

x = −3 + i n t e g r a l 0.5 y = 1.5 + i n t e g r a l v v = i n t e g r a l g ‘ switch ‘

( h i t ‘ snapshot_ ‘ v = λv ’→

l i f t 0 (−v ’ ) + i n t e g r a l g) h i t = when (y<∗ −1.5)

I Nützliche Funktionen:

i n t e g r a l :: Behavior Float → Behavior Float snapshot :: Event a→ Behavior b→ Event (a , b) I Erweiterung: Ball ändert Richtung, wenn er gegen die Wand prallt.

7 [12]

Implementation

I Verhalten, erste Annäherung:

dataBeh1 a = Beh1 ( [ ( UserAction , Time) ]→Time→ a) I Problem:SpeicherleckundIneffizienz

I Analogie: suche insortiertenListen i n L i s t :: [ Int ] → Int →Bool i n L i s t xs y = elem y xs

manyInList ’ :: [ Int ]→ [ Int ]→ [ Bool ] manyInList ’ xs ys = map ( i n L i s t xs ) ys I Besser Sortiertheit direkt nutzen

manyInList :: [ Int ] → [ Int ] → [ Bool ]

8 [12]

(11)

Implementation

I Verhalten werdeninkrementell abgetastet:

dataBeh2 a

= Beh2 ( [ ( UserAction ,Time) ] → [Time] → [ a ] ) I Verbesserungen:

I Zeit doppelt, nureinmal

I Abtastung auchohne Benutzeraktion

I Currying data Behavior a

= Behavior ( ( [Maybe UserAction ] , [ Time] ) → [ a ] ) I Ereignisse sind im Prinzipoptionales Verhalten:

data Event a = Event ( Behaviour (Maybe a))

9 [12]

Längeres Beispiel: Paddleball

I Das Paddel:

paddle = paint red ( t r a n s l a t e ( f s t mouse, −1.7) ( rec 0.5 0.05)) I Der Ball:

pball v e l =

let xvel = v e l ‘stepAccum ‘ xbounce− negate xpos = i n t e g r a l xvel

xbounce = when (xpos>∗ 2 | | ∗xpos<∗ −2) yvel = v e l ‘stepAccum ‘ ybounce− negate ypos = i n t e g r a l yvel

ybounce = when (ypos>∗1.5

| | ∗ypos ‘between ‘ (−2.0,−1.5) &&∗

f s t mouse ‘between ‘ (xpos−0.25,xpos+0.25)) in paint yellow ( t r a n s l a t e (xpos , ypos) ( e l l 0.2 0.2)) I Die Mauern:

walls :: Behavior Picture I . . . und alles zusammen:

paddleball v e l = walls ‘ over ‘ paddle ‘ over ‘ pball v e l

10 [12]

Warum nicht in Scala?

I Lifting und Typklassen fürsyntaktischen Zucker

I Aber: zentrales Konzept sindunendlicheListen (Ströme) mit nicht-strikteAuswertung

I Implementation mit Scala-Listen nicht möglich

I Benötigt:Strömeals unendliche Listen mit effizienter, nicht-strikter Auswertung

I Möglich, aber nicht für diese Vorlesung I Generelle Schwäche:

I Fundamentalnicht-kompositional— ist gibteineHauptfunktion

I Fehlerbehandlung, Nebenläufigkeit?

11 [12]

Zusammenfassung

I Funktional-Reaktive Programmierung am Beispiel FAL (Functional Animation Library)

I Zwei Kernkonzepte: kontinuierlichesVerhaltenund diskreteEreignisse

I Implementiert in Haskell, Systemverhalten als unendlicher Strom von Zuständen

I ErlaubtabstrakteProgammierung vonreaktiven Animationen I Problem ist mangelndeKompositionalität

I Nächste Vorlesungen:Scala!

12 [12]

(12)

Reaktive Programmierung

Vorlesung 4 vom 23.04.15: A Practical Introduction to Scala

Christoph Lüth & Martin Ring Universität Bremen Sommersemester 2015

15:23:44 2015-05-05 1 [17]

Fahrplan

I Teil I: Grundlegende Konzepte

IWas ist Reaktive Programmierung?

INebenläufigkeit und Monaden in Haskell

IFunktional-Reaktive Programmierung

IEinführung in Scala

IDie Scala Collections

IScalaCheck I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

2 [17]

Heute: Scala

I Ascalablelanguage I Rein objektorientiert I Funktional I Eine “JVM-Sprache”

I Seit 2004 von Martin Odersky, EPFL Lausanne (http://www.scala-lang.org/).

I Seit 2011 kommerziell durch Typesafe Inc.

3 [17]

Scala am Beispiel: 01-GCD.scala

Was sehen wir hier?

I Variablen, veränderlich —Mit Vorsicht benutzen!

I Werte, unveränderlich

I while-Schleifen —Unnötig!

I Rekursion — einfache Endrekursion wird optimiert

I Typinferenz — mehr als Java, weniger als Haskell

4 [17]

Scala am Beispiel: 02-Rational.scala

Was sehen wir hier?

I Klassenparameter I this

I Methoden, Syntax für Methodenanwendung I override(nicht optional)

I private Werte und Methoden I Klassenvorbedingunge (require) I Overloading

I Operatoren

5 [17]

Algebraische Datentypen: 03-Expr.scala

Was sehen wir hier?

I case classerzeugt

IFactory-Methode für Konstruktoren

IParameter als impliziteval

Iabgeleitete Implementierung fürtoString,equals

Istrukturelle Gleichheit

I. . . und pattern matching I Pattern sind

Icase 4 =>— Literale

Icase C(4) =>— Konstruktoren

Icase C(x) =>— Variablen

Icase C(_) =>— Wildcards

Icase x: C =>— getypte pattern

Icase C(D(x: T, y), 4) =>— geschachtelt

6 [17]

Implementierung algebraischer Datentypen

Haskell:

data T = C1 | ... | Cn I Ein TypT

I Konstruktoren erzeugen Datentyp

Scala:

T C1

. . . Cn

-

I Varianten alsSubtypen I Problem und Vorteil:

Erweiterbarkeit

I sealedverhindert Erweiterung

7 [17]

Das Typsystem

Behandelt:

I Werte vs. Objekte

I Scala vs. Java

I NULLreferences

8 [17]

(13)

Vererbungshierarchie

Quelle: Odersky, Spoon, Venners:Programming in Scala

9 [17]

Parametrische Polymorphie

I Typparameter (wie in Java, Haskell), Bsp.List[T]

I Problem: Vererbung und Polymorphie

I Ziel: wennS<T, dannList[S]<List[T]

I Problem:Ref.hs I Warum?

IFunktionsraum nicht monoton im ersten Argument

ISeiXY, dannZ−→XZ−→Y, aberX−→Z6⊆Y−→Z

10 [17]

Typvarianz

class C[+T]

I Kovariant I S<T, dann

C[S]<C[T]

I ParameterTnicht in Def.bereich

class C[T]

I Rigide I Kein Subtyping I ParameterTkann

beliebig verwendet werden

class C[-T]

IKontravariant IS<T, dann

C[T]<C[S]

IParameterTnicht in Wertebereich

Beispiel:

class Function[-S, +T] { def apply(x:S) : T }

11 [17]

Traits: 04-Funny.scala

Was sehen wir hier?

I Traits(Mix-ins): abstrakte Klassen, Interfaces; Haskell: Typklassen I Unterschied zu Klassen:

IKeine Parameter

IKeine feste Oberklasse (superdynamisch gebunden) I Nützlich zur Strukturierung:

thin interface+trait=rich interface Beispiel:04-Ordered.scala,04-Rational.scala

12 [17]

Was sind Traits?

I Trait≈Abstrakte Klasse ohne Parameter:

trait Foo[T] { def foo: T

def bar: String = "Hallo"

}

I Erlauben “Mehrfachvererbung”:

class Cextends Foo[Int] with Bar[String] { ... } I Können auch als Mixins verwendet werden:

trait Funny {

def laugh() = println("hahaha") }

(new Cwith Funny).laugh()// hahaha

13 [17]

Implizite Parameter

I Implizite Parameter:

def laugh(implicit stream: PrintStream) = stream.println("hahaha")

I Werden im Kontext des Aufrufs aufgelöst. (Durch den Typen) I Implizite Parameter + Traits≈Typklassen:

traitShow[T] { def show(value: T): String } def print[T](value: T)(implicit show: Show[T]) =

println(show.show(value))

implicit object ShowInt extends Show[Int] { def show(value: Int) = value.toString }

print(7)

14 [17]

Implizite Konversionen

I Implizite Konversionen:

implicit def stringToInt(string: String) = string.toInt val x: Int = "3"

x * "5" == 15 // true

"5" % "4" == 1// true I Mit großer Vorsicht zu genießen!

I “Extension Methods” / “Pimp-My-Library” allerdings sehr nützlich!

I Besser: Implizite Klassen

implicit classRichString(s: String) { def shuffle = Random.shuffle(s.toList)

.mkString }

"Hallo".shuffle // "laoHl"

15 [17]

Scala — Die Sprache

I Objekt-orientiert:

IVeränderlicher, gekapselterZustand

ISubtypen und Vererbung

IKlassen und Objekte I Funktional:

IUnveränderlicheWerte

IPolymorphie

IFunktionen höherer Ordnung

16 [17]

(14)

Beurteilung

I Vorteile:

I Funktional programmieren, in der Java-Welt leben

I Gelungene Integration funktionaler und OO-Konzepte

I Sauberer Sprachentwurf, effiziente Implementierung, reiche Büchereien I Nachteile:

I Manchmal etwaszuviel

I Entwickelt sich ständig weiter

I One-Compiler-Language, vergleichsweise langsam

17 [17]

(15)

Reaktive Programmierung

Vorlesung 5 vom 30.04.15: The Scala Collection Library

Christoph Lüth & Martin Ring Universität Bremen Sommersemester 2015

17:10:28 2015-05-19 1 [24]

Fahrplan

I Teil I: Grundlegende Konzepte

IWas ist Reaktive Programmierung?

INebenläufigkeit und Monaden in Haskell

IFunktional-Reaktive Programmierung

IEinführung in Scala

IDie Scala Collections

IScalaTest und ScalaCheck I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

2 [24]

Heute: Scala Collections

I Sindnichtin die Sprache eingebaut!

I Trotzdem komfortabel

val ages = Map("Homer" -> 36, "Marge" -> 34) ages("Homer") // 36

I Sehr vielseitig (Immutable, Mutable, Linear, Random Access, Read Once, Lazy, Strict, Sorted, Unsorted, Bounded...)

I Und sehr generisch

val a = Array(1,2,3) ++ List(1,2,3) a.flatMap(i ⇒Seq(i,i+1,i+2))

3 [24]

Scala Collections Bücherei

Sehr einheitliche Schnittstellen aber komplexe Bücherei:

4 [24]

Scala Collections Bücherei - Immutable

5 [24]

Scala Collections Bücherei - Mutable

6 [24]

Konstruktoren und Extraktoren

I Einheitliche Konstruktoren:

Traversable(1, 2, 3) Iterable("x", "y","z")

Map("x" -> 24,"y" -> 25, "z" -> 26) Set(Color.red, Color.green, Color.blue) SortedSet("hello","world")

Buffer(x, y, z) IndexedSeq(1.0, 2.0) LinearSeq(a, b, c) ...

I Einheitliche Extraktoren:

val Seq(a,b,c) = Seq(1,2,3) // a = 1; b = 2; c = 3 ...

7 [24]

Exkurs: Funktionen in Scala

I Scala ist rein Objektorientiert.

Ijeder Wert ist ein Objekt

Ijede Operation ist ein Methodenaufruf I Also ist eine Funktion ein Objekt

I und ein Funktionsaufruf ein Methodenaufruf.

traitFunction1[-T1,+R] { def apply(v1: T1): R }

I Syntaktischer Zucker:f(5)wird zuf.apply(5)

8 [24]

(16)

Exkurs: Konstruktoren in Scala

I Der syntaktische Zucker für Funktionen erlaubt uns Konstruktoren ohnenewzu definieren:

trait Person { def age: Int def name: String }

objectPerson {

def apply(a: Int, n: String) = new Person { def age = a

def name = n }

}

val homer = Person(36,"Homer") I Vgl. Case Classes

9 [24]

Exkurs: Extraktoren in Scala

I Das Gegenstück zuapplyistunapply.

Iapply(Konstruktor): Argumente−→Objekt

Iunapply(Extraktor): Objekt−→Argumente I Wichtig für Pattern Matching (Vgl. Case Classes)

objectPerson {

def apply(a: Int, n: String) = <...>

def unapply(p: Person): Option[(Int,String)] = Some((p.age,p.name))

}

homermatch {

case Person(age, name)if age < 18⇒s"hello young

$name"

case Person(_, name) ⇒s"hello old $name"

}

val Person(a,n) = homer

10 [24]

scala.collection.Traversable[+A]

I Super-trait von allen anderen Collections.

I Einzige abstrakte Methode:

def foreach[U](f: Elem⇒U): Unit I Viele wichtige Funktionen sind hier schon definiert:

I ++[B](that: Traversable[B]): Traversable[B]

I map[B](f: A => B): Traversable[B]

I filter(f: A => Boolean): Traversable[A]

I foldLeft[B](z: B)(f: (B,A) => B): B

I flatMap[B](f: A => Traversable[B]): Traversable[B]

I take, drop, exists, head, tail, foreach, size, sum, groupBy, takeWhile ...

I Problem: So funktionieren die Signaturen nicht!

I Die folgende Folie ist für Zuschauer unter 16 Jahren nicht geeignet...

11 [24]

Die wahre Signatur von map

def map[B,That](f: A ⇒B)(implicitbf:

CanBuildFrom[Traversable[A], B, That]): That

Was machen wir damit?

I Schnell wieder vergessen

I Aber im Hinterkopf behalten: Die Signaturen in der Dokumentation sind “geschönt”!

12 [24]

Seq[+A], IndexedSeq[+A], LinearSeq[+A]

I Haben eine länge (length)

I Elemente haben feste Positionen (indexOf, indexOfSlice, ...) I Können Sortiert werden (sorted, sortWith, sortBy, ...) I Können Umgedreht werden (reverse, reverseMap, ...)

I Können mit anderen Sequenzen verglichen werden (startsWith, ...)

I Nützliche Subtypen:List,Stream,Vector,Stack,Queue, mutable.Buffer

I Welche ist die richtige für mich?

http://docs.scala-lang.org/overviews/collections/

performance-characteristics.html

13 [24]

Set[+A]

I Enthalten keine doppelten Elemente

I Unterstützen Vereinigungen, Differenzen, Schnittmengen:

Set("apple","strawberry") ++ Set("apple","peach")

> Set("apple", "strawberry","peach")

Set("apple","strawberry") -- Set("apple","peach")

> Set("strawberry")

Set("apple","strawberry") & Set("apple","peach")

> Set("apple")

I Nützliche Subtypen:SortedSet,BitSet

14 [24]

Map[K,V]

I Ist eine Menge von Schlüssel-Wert-Paaren:

Map[K,V] <: Iterable[(K,V)]

I Ist eine partielle Funktion von Schlüssel zu Wert:

Map[K,V] <: PartialFunction[K,V]

I Werte können “nachgeschlagen” werden:

val ages = Map("Homer" -> 39, "Marge" -> 34) ages("Homer")

> 39

ages isDefinedAt "Bart"// ages contains "Bart"

> false

ages get "Marge"

> Some(34)

I Nützliche Subtypen:mutable.Map

15 [24]

Collections Vergleichen

I Collections sind in Mengen, Maps und Sequenzen aufgeteilt.

I Collections aus verschiendenen Kategorien sind niemals gleich:

Set(1,2,3) == List(1,2,3)// false

I Mengen und Maps sind gleich wenn sie die selben Elemente enthalten:

TreeSet(3,2,1) == HashSet(2,1,3)// true

I Sequenzen sind gleich wenn sie die selben Elemente in der selben Reihenfolge enthalten:

List(1,2,3) == Stream(1,2,3)// true

16 [24]

(17)

Scala Collections by Example - Part I

I Problem: Namen der erwachsenen Personen in einer Liste case classPerson(name: String, age: Int) val persons = List(Person("Homer",39),

Person("Marge",34),

Person("Bart",10), Person("Lisa",8), Person("Maggie",1), Person("Abe",80))

I Lösung:

val adults = persons.filter(_.age >= 18).map(_.name)

> List("Homer","Marge", "Abe")

17 [24]

Scala Collections by Example - Part II

I Problem: Fibonacci Zahlen so elegant wie in Haskell?

fibs = 0 : 1 : zipWith (+) fibs (tail fibs) I Lösung:

val fibs: Stream[BigInt] =

BigInt(0)#:: BigInt(1) #:: fibs.zip(fibs.tail).map(

n ⇒n._1 + n._2)

fibs.take(10).foreach(println)

> 0

> 1

> ...

> 21

> 34

18 [24]

Option[+A]

I Habenmaximal1 Element sealed trait Option[+A]

case object None extends Option[Nothing]

case classSome(get: A) extends Option[A]

I EntsprechenMaybein Haskell

I Sollten dort benutzt werden wo in Javanullim Spiel ist def get(elem: String) = elem match{

case "a" ⇒Some(1) case "b" ⇒Some(2) case _ ⇒None }

I Hilfreich dabei:

Option("Hallo")// Some("Hallo") Option(null) // None

19 [24]

Option[+A]

I An vielen Stellen in der Standardbücherei gibt es die Auswahl:

val ages = Map("Homer" -> 39,"Marge" -> 34) ages("Bart")// NoSuchElementException ages.get("Bart")// None

I Nützliche Operationen aufOption val x: Option[Int] = ???

x getOrElse 0

x foldLeft ("Test")(_.toString) x exists (_ == 4)

...

20 [24]

Ranges

I Repräsentieren Zahlensequenzen

class Range(start: Int, end: Int, step: Int) class Inclusive(start: Int, end: Int, step: Int)

extends Range(start,end + 1,step) I Intist “gepimpt” (RichInt):

1 to 10 // new Inclusive(1,10,1) 1 to (10,5) // new Inclusive(1,10,5) 1 until 10// new Range(1,10) I Werte sind berechnet und nicht gespeichert I Keine “echten” Collections

I Dienen zum effizienten Durchlaufen von Zahlensequenzen:

(1 to 10).foreach(println)

21 [24]

For Comprehensions

I In Scala istfornur syntaktischer Zucker for (i←1 to 10) println(i)

⇒(1 to 10).foreach(i ⇒println(i)) for (i←1 to 10) yieldi * 2

⇒(1 to 10).map(i ⇒i * 2)

for (i←1 to 10 ifi > 5) yieldi * 2

⇒(1 to 10).filter(i⇒i > 5).map(i ⇒i * 2) for (x←1 to 10, y←1 to 10) yield (x,y)

⇒(1 to 10).flatMap(x ⇒(1 to 10).map(y ⇒(x,y))) I Funktioniert mit allen Typen die die nötige Untermenge der

Funktionen (foreach,map,flatMap,withFilter) implementieren.

22 [24]

Scala Collections by Example - Part III

I Problem: Wörter in allen Zeilen in allen Dateien in einem Verzeichnis durchsuchen.

def files(path: String): List[File]

def lines(file: File): List[String]

def words(line: String): List[String]

def find(path: String, p: String ⇒Boolean) = ???

I Lösung:

def find(path: String, p: String ⇒Boolean) =for { file ←files(path)

line ←lines(file)

word ←words(line) ifp(word) } yieldword

23 [24]

Zusammenfassung

I Scala Collections sind ziemlich komplex I Dafür sind die Operationen sehr generisch

I Es gibt keine in die Sprache eingebauten Collections:

Die Collections in der Standardbücherei könnte man alle selbst implementieren

I Für fast jeden Anwendungsfall gibt es schon einen passenden Collection Typ

I for-Comprehensions sind in Scala nur syntaktischer Zucker I Nächstes mal: Testen in Scala

24 [24]

(18)

Reaktive Programmierung

Vorlesung 6 vom 05.05.15: ScalaTest and ScalaCheck

Christoph Lüth & Martin Ring Universität Bremen Sommersemester 2015

17:10:29 2015-05-19 1 [25]

Organisatorisches

I Zu diskutieren:

IVorlesung ab jetzt Dienstags von 16-18 Uhr,

IÜbung dafür Donnerstags ab 9 Uhr?

2 [25]

Fahrplan

I Teil I: Grundlegende Konzepte

I Was ist Reaktive Programmierung?

I Nebenläufigkeit und Monaden in Haskell

I Funktional-Reaktive Programmierung

I Einführung in Scala

I Die Scala Collections

I ScalaTest und ScalaCheck

I Teil II: Nebenläufigkeit

I Teil III: Fortgeschrittene Konzepte

3 [25]

Was ist eigentlich Testen?

Myers, 1979

Testing is the process of executing a program or system with the intent of finding errors.

I Hier: testen isselektive,kontrollierteProgrammausführung.

I Zieldes Testens ist es immer, Fehler zu finden wie:

IDiskrepanz zwischen Spezifikation und Implementation

Istrukturelle Fehler, die zu einem fehlerhaften Verhalten führen (Programmabbruch, Ausnahmen, etc)

E. W. Dijkstra, 1972

Program testing can be used to show the presence of bugs, but never to show their absence.

4 [25]

Testmethoden

I Statisch vs. dynamisch:

I StatischeTestsanalysierenden Quellcode ohne ihn auszuführen (statische Programmanalyse)

I DynamischeTests führen das Programm unterkontrolliertenBedingungen aus, und prüfen das Ergebnis gegen eine gegebene Spezifikation.

I Zentrale Frage: wo kommen dieTestfälleher?

I Black-box: Struktur des s.u.t. (hier: Quellcode) unbekannt, Testfälle werden aus der Spezifikation generiert;

I Grey-box: Teile der Struktur des s.u.t. ist bekannt (z.B. Modulstruktur)

I White-box: Struktur des s.u.t. ist offen, Testfälle werden aus dem Quellcode abgeleitet

5 [25]

Spezialfall des Black-Box-Tests: Monte-Carlo Tests

I Bei Monte-Carlo oder Zufallstests werdenzufälligeEingabewerte generiert, und das Ergebnis gegen eine Spezifikation geprüft.

I Dies erfordertausführbareSpezifikationen.

I Wichtig ist dieVerteilungder Eingabewerte.

IGleichverteilt über erwartete Eingaben, Grenzfälle beachten.

I Funktioniert gut mithigh-level-Spachen(Java, Scala, Haskell)

IDatentypen repräsentieren Informationen aufabstrakterEbene

IEigenschaft gutspezifizierbar

IBeispiel: Listen, Listenumkehr in C, Java, Scala I Zentrale Fragen:

IWie können wirausführbare Eigenschaftenformulieren?

IWieVerteilungder Zufallswerte steuern?

6 [25]

ScalaTest

I Test Framework für Scala

importorg.scalatest.FlatSpec class StringSpecextends FlatSpec {

"A String" should"reverse" in {

"Hello".reverse should be ("olleH") }

it should "return the correct length" in {

"Hello".length should be (5) }

}

7 [25]

ScalaTest Assertions 1

I ScalaTest Assertions sind Makros:

importorg.scalatest.Assertions._

val left = 2 val right = 1 assert(left == right)

I Schlägt fehl mit"2 did not equal 1"

I Alternativ:

val a = 5 val b = 2 assertResult(2) {

a - b }

I Schlägt fehl mit"Expected 2, but got 3"

8 [25]

(19)

ScalaTest Assertions 2

I Fehler manuell werfen:

fail("I’ve got a bad feeling about this") I Erwartete Exeptions:

val s = "hi"

val e = intercept[IndexOutOfBoundsException] { s.charAt(-1)

}

I Assumptions

assume(database.isAvailable)

9 [25]

ScalaTest Matchers

I Gleichheit überprüfen:

result should equal (3) result should be (3) result shouldBe 3 result shouldEqual 3 I Länge prüfen:

result should have length 3 result should have size 3 I Und so weiter...

text should startWith ("Hello") result should be a [List[Int]]

list should contain noneOf (3,4,5)

I Siehehttp://www.scalatest.org/user_guide/using_matchers

10 [25]

ScalaTest Styles

I ScalaTest hat viele verschiedene Styles, die über Traits eingemischt werden können

I Beispiel:FunSpec(Ähnlich wie RSpec) class SetSpec extends FunSpec {

describe("A Set") { describe("when empty") {

it("should have size 0") { assert(Set.empty.size == 0) }

it("should produce NoSuchElementException when head is invoked") {

intercept[NoSuchElementException] { Set.empty.head

} } } } } I Übersicht unter

http://www.scalatest.org/user_guide/selecting_a_style 11 [25]

Blackbox Test

I Überprüfen eines Programms oder einer Funktion ohne deren Implementierung zu nutzen:

def primeFactors(n: Int): List[Int] = ???

I z.B.

"primeFactors"should"work for 360"in {

primeFactors(360) should contain theSameElementsAs List(2,2,2,3,3,5)

}

I Was ist mit allen anderen Eingaben?

12 [25]

Property based Testing

I Überprüfen vonEigenschaften(Properties) eines Programms / einer Funktion:

def primeFactors(n: Int): List[Int] = ???

I Wir würden gerne so was schreiben:

forall x >= 1 -> primeFactors(x).product = x

&& primeFactors(x).forall(isPrime)

I Aber wo kommen die Eingaben her?

13 [25]

Testen mit Zufallswerten

I def primeFactors(n: Int): List[Int] = ???

I Zufallszahlen sind doch einfach!

"primeFactors"should"work for many numbers" in { (1 to 1000) foreach { _ ⇒

val x = Math.max(1, Random.nextInt.abs) assert(primeFactors(x).product == (x)) assert(primeFactors(x).forall(isPrime)) }

}

I Was ist mit dieser Funktion?

def sum(list: List[Int]): Int = ???

14 [25]

ScalaCheck

I ScalaCheck nutzt Generatoren um Testwerte für Properties zu generieren

forAll { (list: List[Int])⇒

sum(list) == list.foldLeft(0)(_ + _) }

I Generatoren werden über implicits aufgelöst

I TypklasseArbitraryfür viele Typen vordefiniert:

abstract classArbitrary[T] { val arbitrary: Gen[T]

}

15 [25]

Zufallsgeneratoren

I Ein generischer Zufallsgenerator:

traitGenerator[+T] { def generate: T } objectGenerator {

def apply[T](f: ⇒T) = new Generator[T] { def generate = f }

}

I val integers = Generator(Random.nextInt)

I val booleans = Generator(integers.generate > 0)

I val pairs =

Generator((integers.generate,integers.generate))

16 [25]

(20)

Zufallsgeneratoren Kombinieren

I Ein generischer,kombinierbarerZufallsgenerator:

trait Generator[+T] { self⇒ def generate: T

def map[U](f: T ⇒U) = new Generator[U] { def generate = f(self.generate)

}

def flatMap[U](f: T ⇒Generator[U]) = new Generator[U] {

def generate = f(self.generate).generate }

}

17 [25]

Einfache Zufallsgeneratoren

I Einelementige Wertemenge:

def single[T](value: T) = Generator(value)

I Eingeschränkter Wertebereich:

def choose(lo: Int, hi: Int) = integers.map(x⇒lo + x % (hi - lo))

I Aufzählbare Wertemenge:

def oneOf[T](xs: T*): Generator[T] = choose(0,xs.length).map(xs)

18 [25]

Beispiel: Listen Generieren

I Listen haben zwei Konstruktoren:Nilund:::

def lists: Generator[List[Int]] = for { isEmpty ←booleans

list ←if(isEmpty) emptyLists else nonEmptyLists }

I Die Menge der leeren Listen enthält genau ein Element:

def emptyLists = single(Nil)

I Nicht-leere Listen bestehen aus einem Element und einer Liste:

def nonEmptyLists = for { head ←integers tail ←lists } yieldhead :: tail

19 [25]

ScalaCheck

I ScalaCheck nutzt Generatoren um Testwerte für Properties zu generieren

forAll { (list: List[Int])⇒

sum(list) == list.foldLeft(0)(_ + _) }

I Generatoren werden über implicits aufgelöst

I TypklasseArbitraryfür viele Typen vordefiniert:

abstract classArbitrary[T] { val arbitrary: Gen[T]

}

20 [25]

Kombinatoren in ScalaCheck

object Gen {

def choose[T](min: T, max: T)(implicit c: Choose[T]):

Gen[T]

def oneOf[T](xs: Seq[T]): Gen[T]

def sized[T](f: Int ⇒Gen[T]): Gen[T]

def someOf[T](gs: Gen[T]*); Gen[Seq[T]]

def option[T](g: Gen[T]): Gen[Option[T]]

...

}

trait Gen[+T] {

def map[U](f: T ⇒U): Gen[U]

def flatMap[U](f: T ⇒Gen[U]): Gen[U]

def filter(f: T ⇒Boolean): Gen[T]

def suchThat(f: T⇒Boolean): Gen[T]

def label(l: String): Gen[T]

def |(that: Gen[T]): Gen[T]

...

}

21 [25]

Wertemenge einschränken

I Problem: Vorbedingungen können dazu führen, dass nur wenige Werte verwendet werden können:

val prop = forAll { (l1: List[Int], l2: List[Int])⇒ l1.length == l2.length =⇒ l1.zip(l2).unzip() ==

(l1,l2) }

scala> prop.check

Gave up after only 4 passed tests. 500 tests were discarded.

I Besser:

forAll(myListPairGenerator) { (l1, l2)⇒ l1.zip(l2).unzip() == (l1,l2)

}

22 [25]

Kombinatoren für Properties

I Properties können miteinander kombiniert werden:

val p1 = forAll(...) val p2 = forAll(...) val p3 = p1 && p2 val p4 = p1 || p2 val p5 = p1 == p2 val p6 = all(p1, p2) val p7 = atLeastOne(p1, p2)

23 [25]

ScalaCheck in ScalaTest

I Der TraitCheckerserlaubt es, ScalaCheck in beliebigen ScalaTest Suiten zu verwenden:

classIntListSpec extends FlatSpec with PropertyChecks {

"Any list of integers"should "return its correct sum" in {

forall { (x: List[Int]) ⇒x.sum == x.foldLeft(0)(_

+ _) } }

}

24 [25]

(21)

Zusammenfassung

I ScalaTest: DSL für Tests in Scala

I Verschiedene Test-Stile durch verschiedene Traits

I Matchers um Assertions zu formulieren I ScalaCheck: Property-based testing

I Gen[+T]um Zufallswerte zu generieren

I Generatoren sind ein monadischer Datentyp

I TypklasseArbitrary[+T]stellt generatoren implizit zur Verfügung I Nächstes mal endlich Nebenläufigkeit: Futures und Promises

25 [25]

Referenzen

ÄHNLICHE DOKUMENTE

6 If a Publisher signals either onError or onComplete on a Subscriber, that Subscriber’s Subscription MUST be considered cancelled. RP SS 2019

I Zugriff auf State nur über elementare Operationen. RP SS 2019

subscribe MUST call onSubscribe on the provided Subscriber prior to any other signals to that Subscriber and MUST return normally, except when the provided Subscriber is null in

If a Publisher signals either onError or onComplete on a Subscriber, that Subscriber’s Subscription MUST be considered cancelled. RP SS 2017

I Wird durch Haskell Tysystem unterstützt (keine UI ohne IO) I Nützlich für andere UI mit Kontrollumkehr. I Beispiel: ein GUI für das Wörterratespiel

Subscriber prior to any other signals to that Subscriber and MUST return normally, except when the provided Subscriber is null in which case it MUST throw

Kuna r¨ uhm on inversne poolr¨ uhm, siis lause 4.7 t˜ ottu on tegemist peaaegu k-p¨ o¨ ordinversse poolr¨ uhmaga.. Niisiis, peaaegu k-p¨ o¨ ordinverssuse tingimus muudab poolv˜

l Assisted Voluntary Return and Reintegration programmes (AVRRs) is one way to deal with irregular migration.. l While AVRR is an increasing policy priority,