• Keine Ergebnisse gefunden

Reaktive Programmierung Vorlesung 1 vom 14.04.15: Was ist Reaktive Programmierung?

N/A
N/A
Protected

Academic year: 2022

Aktie "Reaktive Programmierung Vorlesung 1 vom 14.04.15: Was ist Reaktive Programmierung?"

Copied!
509
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

(2)

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:

I Voraussichtlich 6 Übungsblätter

I Alle bearbeitet, insgesamt 40% (Notenspiegel PI3)

I Übungsgruppen 2 – 4 Mitglieder

I Fachgespräch am Ende

(3)

Warum Reaktive Programmierung?

Herkömmliche

Programmiersprachen:

I C, C++

I JavaScript, Ruby, PHP, Python

I Java

I (Haskell) Eigenschaften:

I Imperativund prozedural

I Sequentiell

Zugrundeliegendes Paradigma:

(4)

Warum Reaktive Programmierung?

Herkömmliche

Programmiersprachen:

I C, C++

I JavaScript, Ruby, PHP, Python

I Java

I (Haskell) Eigenschaften:

I Imperativund prozedural

I Sequentiell

Zugrundeliegendes Paradigma:

. . . aber die Welt ändert sich:

I Das Netzverbindet Rechner

I Selbst eingebettete Systeme sind vernetzt (Auto: ca. 100 Proz.)

I Mikroprozessoren sindmehrkernig

I Systeme sind eingebettet, nebenläufig,reagieren auf ihre Umwelt.

(5)

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

I Problem:Nebenläufigkeit

I Nebenläufigkeit verursacht Synchronisationsprobleme

I Behandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte: Locks, Semaphoren, Monitore

(6)

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

I Problem: Nebenläufigkeit

I Nebenläufigkeit verursacht Synchronisationsprobleme

I Behandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte: Locks, Semaphoren, Monitore

(7)

Probleme mit dem herkömmlichen Ansatz

Programm

Daten Eingabe

Ausgabe

Daten Programm

Eingabe Ausgabe

Daten Programm

Eingabe Ausgabe

Eingabe Ausgabe

Daten Programm

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

I Problem: Nebenläufigkeit

I Nebenläufigkeit verursacht Synchronisationsprobleme

I Behandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte: Locks, Semaphoren, Monitore

(8)

Probleme mit dem herkömmlichen Ansatz

Programm

Daten Eingabe

Ausgabe

Daten Programm

Eingabe Ausgabe

Daten Programm

Eingabe Ausgabe

Eingabe Ausgabe

Daten Programm

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

I Problem: Nebenläufigkeit

I Nebenläufigkeit verursacht Synchronisationsprobleme

I Behandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte: Locks, Semaphoren, Monitore

(9)

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

I Problem: Nebenläufigkeit

I Nebenläufigkeit verursacht Synchronisationsprobleme

I Behandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte:

Locks, Semaphoren, Monitore

(10)

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

I Problem: Nebenläufigkeit

I Nebenläufigkeit verursacht Synchronisationsprobleme

I Behandlung:

I Callbacks (JavaScript)

I Events (Java)

I Global Locks (Python, Ruby)

I Programmiersprachenkonstrukte:

Locks, Semaphoren, Monitore

(11)

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

(12)

The Reactive Manifesto

I http://www.reactivemanifesto.org/

Responsive

Resilient

Message Driven Elastic

(13)

Was ist Reaktive Programmierung?

I ImperativeProgrammierung: Zustandsübergang

I Prozeduralund OO: Verkapselter Zustand

I FunktionaleProgrammierung: Abbildung (mathematische Funktion)

I ReaktiveProgrammierung:

1. Datenabhängigkeit

2. Reaktiv=funktional+nebenläufig

(14)

Datenflusssprachen (data flow languages)

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

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

I KeineZuweisungen, sondernDatenfluss

I Synchron:alle Aktionen ohne Zeitverzug

I Verwendung in der Luftfahrtindustrie (Airbus)

(15)

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

(16)

Fahrplan

I Teil I: Grundlegende Konzepte

I Teil II: Nebenläufigkeit

I Futures and Promises

I Reaktive Datenströme I

I Reaktive Datenströme II

I Das Aktorenmodell

I Aktoren und Akka

I Teil III: Fortgeschrittene Konzepte

(17)

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

(18)

Rückblick Haskell

I Definition von Funktionen:

I lokale Definitionen mitletundwhere

I Fallunterscheidung undguarded equations

I Abseitsregel

I Funktionen höherer Ordnung

I Typen:

I Basisdatentypen:Int,Integer,Rational,Double,Char,Bool

I Strukturierte Datentypen:[a],(a, b)

I Algebraische Datentypen:data Maybe a = Just a | Nothing

(19)

Rückblick Haskell

I Abstrakte Datentypen

I Module

I Typklassen

I Verzögerte Auswertung und unendliche Datentypen

(20)

Ein- und Ausgabe in Haskell

Umwelt Haskell

Aktionen

Umwelt Reine

Funktionen Haskell

Problem:

I Funktionen mit Seiteneffekten nicht referentiell transparent.

I readString :: . . .→String ??

Lösung:

I Seiteneffekte am Typ erkennbar

I Aktionenkönnen nurmit Aktionenkomponiert werden

I „einmal Aktion, immer Aktion“

(21)

Ein- und Ausgabe in Haskell

Umwelt Haskell

Aktionen

Umwelt Reine

Funktionen Haskell

Problem:

I Funktionen mit Seiteneffekten nicht referentiell transparent.

I readString :: . . .→String ??

Lösung:

I Seiteneffekte am Typ erkennbar

I Aktionenkönnen nurmit Aktionenkomponiert werden

I „einmal Aktion, immer Aktion“

(22)

Aktionen als abstrakter Datentyp

I ADT mit OperationenKomposition und Lifting

I Signatur:

type IO α

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

I Pluselementare Operationen (lesen, schreiben etc)

(23)

Elementare Aktionen

I Zeile von stdin lesen:

getLine :: IO String

I Zeichenkette auf stdout ausgeben:

putStr :: String→ IO ()

I Zeichenkette mit Zeilenvorschubausgeben:

putStrLn :: String→ IO ()

(24)

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

(25)

Noch ein Beispiel

I Umgekehrtes Echo:

ohce :: IO () ohce = getLine

=λs→ putStrLn ( reverse s ) ohce

I Was passiert hier?

I ReineFunktion reverse wird innerhalb vonAktionputStrLngenutzt

I FolgeaktionohcebenötigtWertder vorherigen Aktion nicht

I Abkürzung:

pq = p=λ_ q

(26)

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.

(27)

Drittes Beispiel

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

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

i f s 6= "" then do

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

else return ()

I Was passiert hier?

I KombinationausKontrollstrukturenundAktionen

I AktionenalsWerte

I Geschachteltedo-Notation

(28)

Module in der Standardbücherei

I Ein/Ausgabe, Fehlerbehandlung (ModulIO)

I Zufallszahlen (ModulRandom)

I Kommandozeile, Umgebungsvariablen (ModulSystem)

I Zugriff auf das Dateisystem (Modul Directory)

I Zeit (ModulTime)

(29)

Ein/Ausgabe mit Dateien

I ImPreludevordefiniert:

I Dateien 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 Modul IOder Standardbücherei

I Buffered/Unbuffered, Seeking, &c.

I Operationen aufHandle

(30)

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

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

do cont ← 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

(31)

Aktionen als Werte

I Aktionensind Wertewie alle anderen.

I DadurchDefinitionvon Kontrollstrukturenmöglich.

I Endlosschleife:

forever :: IO α→ IO α forever a = a forever a

I Iteration (feste Anzahl):

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

| otherwise = aforN (n−1) a

I VordefinierteKontrollstrukturen (Control .Monad):

I when,mapM,forM,sequence, . . .

(32)

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 ]

(33)

Fehlerbehandlung

I Fehlerwerden durch Exceptionrepräsentiert

I ExceptionistTypklasse— kann durch eigene Instanzen erweitert werden

I Vordefinierte 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, try für erwartete

I Fehlerbehandlungnur in Aktionen

(34)

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 IOError kann analysiert werden (sieheSystem.IO.Error)

I readmit Ausnahme bei Fehler (statt Programmabbruch):

readIO :: Read a⇒ String→ IO a

(35)

Ausführbare Programme

I Eigenständiges Programm istAktion

I Hauptaktion:main :: IO () in ModulMain

I wcals eigenständiges Programm:

module Main where

import System . Environment ( getArgs ) import Control . Exception

. . .

main :: IO () main = do

args ← getArgs mapM_ wc2 args

(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 =

do l 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 ’ ) )

(37)

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 =

do l 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 ’ ) )

(38)

Funktionen mit Zustand

I Idee: Seiteneffektexplizit machen

I Funktionf :AB mit Seiteneffekt in ZustandS:

f :A×SB×S

∼=

f :ASB×S

I Datentyp:SB×S

I Komposition: Funktionskomposition unduncurry

(39)

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

(40)

Beispiel: Ein Zähler

I Datentyp:

type WithCounter α = 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 ()

(41)

Implizite vs. explizite Zustände

I Nachteil: Zustand istexplizit

I Kanndupliziertwerden

I Daher: Zustandimplizit machen

I Datentypverkapseln

I Signatur State , comp, lift, elementare Operationen

I Beispiel für eineMonade

I Generische Datenstruktur, dieVerkettungvonBerechnungenerlaubt

(42)

Aktionen als Zustandstransformationen

I Idee: Aktionen sindTransformationen auf Systemzustand S

I S beinhaltet

I Speicher als AbbildungA*V (AdressenA, WerteV)

I Zustand des Dateisystems

I Zustand des Zufallsgenerators

I In Haskell: TypRealWorld

type IO α= State RealWorld α −−... oder so ähnlich

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

I Entscheidend nurReihenfolgeder Aktionen

(43)

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

(44)

Zusammenfassung

I Reaktive Programmierung: Beschreibung derAbhängigkeitvon Daten

I Rückblick Haskell:

I Abhängigkeit von Aussenwelt in TypIO kenntlich

I Benutzung von IO: vordefinierte Funktionen in der Haskell98 Bücherei

I Werte vom TypIO(Aktionen) können kombiniert werden wie alle anderen

I Nächstes Mal:

I Monaden und Nebenläufigkeit in Haskell

(45)

Reaktive Programmierung

Vorlesung 2 vom 16.04.15: Monaden und Nebenlaeufigkeit in Haskell

Christoph Lüth & Martin Ring

Universität Bremen

Sommersemester 2015

(46)

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

(47)

Speisekarte

I Das Geheimnis der Monade

I Concurrent Haskell

(48)

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:

I Systemzustand darfnie dupliziertodervergessenwerden.

I Auswertungsreihenfolge muss erhalten bleiben.

I KompositionmussInvarianten erhalten Zustandsübergangsmonaden

(49)

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 Damit f =g =uncurry g◦f

I Aber:ST kannkein Typsynonymsein

I Nötig:abstrakter Datentyp um Invariantenzu erhalten

(50)

ST als Abstrakter Datentyp

I Datentyp verkapseln:

newtype ST 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 ))

(51)

Aktionen

I Aktionen: Zustandstransformationen auf der Welt

I TypRealWorld#repräsentiert Außenwelt

I Typ hat genau einen Wert realworld #, 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 TypIOstellt lediglichReihenfolge sicher.

(52)

Was ist eigentlich eine Monade?

I STmodelliertimperative Konzepte.

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

I Ausnahmen:f :: a-> Maybe bmit Komposition

(=) :: Maybe a→ (a→ Maybe b)→ Maybe b Just a = f = f a

Nothing= f = Nothing

(53)

Monads: The Inside Story

class Monad 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

(54)

Beispiel: Speicher und Referenzen

I Signatur:

typeMem a

instance Mem Monad

I Referenzen sind abstrakt:

type Ref

newRef :: Mem Ref

I Speicher liest/schreibt String: readRef :: Ref → Mem String writeRef :: Ref → String→ Mem ()

(55)

Implementation der Referenzen

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

type Mem = 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 [ ] )

(56)

IORef — Referenzen

I Datentyp der Standardbücherei (GHC) import Data . IORef

data IORef 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.

(57)

Beispiel: Referenzen

fac :: Int→ IO Int

fac x = do acc ← newIORef 1 loop acc x where

loop acc 0 = readIORef acc

loop acc n =do t ← readIORef acc writeIORef acc ( t∗ n) loop acc (n−1)

(58)

Die Identitätsmonade

I Die allereinfachste Monade:

type Id a = a

instance Monad Id where return a = a

b= f = f b

(59)

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?

(60)

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 instance Monad ( 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

(61)

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, . . .

(62)

Konzepte der Nebenläufigkeit

I Thread (lightweight process) vs. Prozess

Programmiersprache/Betriebssystem Betriebssystem (z.B. Java, Haskell, Linux)

gemeinsamer Speicher getrennter Speicher

Erzeugung billig Erzeugung teuer

mehrere pro Programm einerpro Programm

I Multitasking:

I präemptiv:Kontextwechsel wirderzwungen

I kooperativ:Kontextwechsel nurfreiwillig

(63)

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

(64)

Threads in Haskell: Concurrent Haskell

I SequentiellesHaskell: Reduktion eines Ausdrucks

I Auswertung

I NebenläufigesHaskell: Reduktion eines Ausdrucks anmehreren Stellen

I ghcimplementiert Haskell-Threads

I ModulControl.Concurrententhält Basisfunktionen

I Wenige Basisprimitive, darauf aufbauend Abstraktionen

(65)

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 ()

(66)

Rahmenbedingungen

I Zeitscheiben:

I Tick: Default 20ms

I Contextswitchpro Tick bei Heapallokation

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

I Blockierung:

I Systemaufrufe blockierenalle Threads

I Mit threaded library (-threaded) nicht alle

I Aber: Haskell Standard-IO blockiertnur den aufrufenden Thread

(67)

Concurrent Haskell — erste Schritte

I Ein einfaches Beispiel:

write :: Char→ IO ()

write c = putChar c write c main :: IO ()

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

I Ausgabeghc: (X|O)

(68)

Synchronisation mit MVars

I Basissynchronisationmechanismusin Concurrent Haskell

I Alles andereabgeleitet

I MVar averänderbareVariable (vgl. IORef a)

I Entwederleeroder gefülltmit Wert vom Typ a

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)

NB.Aufwecken blockierter ProzesseeinzelninFIFO

(69)

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 ()

(70)

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

I Achtung:race conditions

(71)

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:MVar synchronisiert Eingabe

(72)

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:MVar synchronisiert Eingabe

(73)

Ein einfaches Beispiel mit Synchronisation

I MVarvoll⇔ Eingabe möglich

I Also: 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 = do f l a g ← newMVar ()

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

(74)

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 ()

(75)

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)) ())

(76)

Speisende Philosophen

I Hauptfunktion:n Stäbchen erzeugen

I Anzahl Philosophen in der Kommandozeile main = do

a :_ ← getArgs let num= read a

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

block

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

block = newEmptyMVar= takeMVar

(77)

Zusammenfassung

I Monaden und andere Kuriositäten

I Zustandsmonade - Referenzen

I Fehlermonaden

I Concurrent Haskellbietet

I Threadsauf Quellsprachenebene

I Synchronisierung mitMVars

I Durchschlankes Designeinfache Implementierung

I Funktionales Paradigma erlaubtAbstraktionen

I Beispiel:Semaphoren

I Nächste Woche: Funktional-Reaktive Programmierung.

(78)

Reaktive Programmierung

Vorlesung 3 vom 21.04.15: Funktional-Reaktive Programmierung

Christoph Lüth & Martin Ring

Universität Bremen

Sommersemester 2015

(79)

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

(80)

Das Tagemenü

I Funktional-Reaktive Programmierung(FRP) istrein funktionale, reaktive Programmierung.

I Sehrabstraktes Konzept — 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.

(81)

FRP in a Nutshell

I Zwei Basiskonzepte

I Kontinuierliches, über der Zeit veränderlichesVerhalten:

type Time = Float

type Behaviour a = Time → a

I Diskrete Ereignissezu einem bestimmten Zeitpunkt:

type Event a = [ (Time, a ) ]

I Obige Typdefinitionen sindSpezifikation, nicht Implementation

(82)

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, . . . )

(83)

Reaktive Animationen: Verhaltensänderung

I Beispiel: auf Knopfdruck Farbe ändern:

color1 :: Behavior Color

color1 = red ‘ untilB ‘ lbp− blue

I Was passiert hier?

I untilB kombiniert Verhalten:

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

I =istmapfür Ereignisse:

(=) :: Event a (a→b) Event b () :: Event a→ b→ Event b

ev = e =λ_ v

I Kombination von Ereignissen:

(84)

Reaktive Animationen: Verhaltensänderung

I Beispiel: auf Knopfdruck Farbe ändern:

color2r = red ‘ untilB ‘ ce where

ce = ( lbp − blue ‘ untilB ‘ ce ) .|. (key− yellow ‘ untilB ‘ ce )

I Was passiert hier?

I untilB kombiniert Verhalten:

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

I =istmapfür Ereignisse:

(=) :: Event a (a→b) Event b () :: Event a→ b→ Event b

ev = e =λ_ v Kombination von Ereignissen:

(85)

Der Springende Ball

ball2 = paint red ( t r a n s l a t e (x , y) ( e l l 0.2 0.2)) where g = −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.

(86)

Implementation

I Verhalten, erste Annäherung:

data Beh1 a = Beh1 ( [ ( UserAction , Time) ]→ Time→ a)

I Problem:Speicherleck und Ineffizienz

I Analogie: suche insortierten Listen 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 ]

(87)

Implementation

I Verhalten werdeninkrementell abgetastet:

data Beh2 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))

(88)

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 ‘ xbouncenegate xpos = i n t e g r a l xvel

xbounce = when (xpos > 2 | | ∗ xpos <∗ −2) yvel = v e l ‘stepAccum ‘ ybouncenegate 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

(89)

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?

(90)

Zusammenfassung

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

I Zwei Kernkonzepte: kontinuierlichesVerhalten und diskreteEreignisse

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

I Erlaubtabstrakte Progammierung vonreaktiven Animationen

I Problem ist mangelndeKompositionalität

I Nächste Vorlesungen:Scala!

(91)

Reaktive Programmierung

Vorlesung 4 vom 23.04.15: A Practical Introduction to Scala

Christoph Lüth & Martin Ring

Universität Bremen

Sommersemester 2015

(92)

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

(93)

Heute: Scala

I Ascalable language

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.

(94)

Scala am Beispiel: 01-GCD.scala

Was sehen wir hier?

I Variablen, veränderlich

I Werte, unveränderlich

I while-Schleifen

I Rekursion — einfache Endrekursion wird optimiert

I Typinferenz — mehr als Java, weniger als Haskell

(95)

Scala am Beispiel: 01-GCD.scala

Was sehen wir hier?

I Variablen, veränderlich

I Werte, unveränderlich

I while-Schleifen

I Rekursion — einfache Endrekursion wird optimiert

I Typinferenz — mehr als Java, weniger als Haskell

(96)

Scala am Beispiel: 01-GCD.scala

Was sehen wir hier?

I Variablen, veränderlich —Mit Vorsicht benutzen!

I Werte, unveränderlich

I while-Schleifen

I Rekursion — einfache Endrekursion wird optimiert

I Typinferenz — mehr als Java, weniger als Haskell

(97)

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

(98)

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

(99)

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

(100)

Algebraische Datentypen: 03-Expr.scala

Was sehen wir hier?

I case classerzeugt

I Factory-Methode für Konstruktoren

I Parameter als impliziteval

I abgeleitete Implementierung fürtoString,equals

I strukturelle Gleichheit

I . . . und pattern matching

I Pattern sind

I case 4 =>— Literale

I case C(4) =>— Konstruktoren

I case C(x) =>— Variablen

I case C(_) =>— Wildcards

I case x: C =>— getypte pattern

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

(101)

Algebraische Datentypen: 03-Expr.scala

Was sehen wir hier?

I case classerzeugt

I Factory-Methode für Konstruktoren

I Parameter als impliziteval

I abgeleitete Implementierung fürtoString,equals

I strukturelle Gleichheit

I . . . und pattern matching

I Pattern sind

I case 4 =>— Literale

I case C(4) =>— Konstruktoren

I case C(x) =>— Variablen

I case C(_) =>— Wildcards

I case x: C =>— getypte pattern

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

(102)

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

(103)

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

(104)

Das Typsystem

Behandelt:

I Werte vs. Objekte

I Scala vs. Java

I NULLreferences

(105)

Vererbungshierarchie

Quelle: Odersky, Spoon, Venners:Programming in Scala

(106)

Parametrische Polymorphie

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

I Problem: Vererbung und Polymorphie

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

I Problem:Ref.hs

I Warum?

I Funktionsraum nicht monoton im ersten Argument

I SeiX Y, dann Z −→X Z −→Y, aberX −→Z 6⊆Y −→Z

(107)

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 Parameter Tkann beliebig verwendet werden

class C[-T]

I Kontravariant

I S<T, dann C[T]<C[S]

I ParameterTnicht in Wertebereich Beispiel:

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

(108)

Traits: 04-Funny.scala

Was sehen wir hier?

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

I Unterschied zu Klassen:

I Keine Parameter

I Keine feste Oberklasse (superdynamisch gebunden)

I Nützlich zur Strukturierung:

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

(109)

Was sind Traits?

I Trait≈Abstrakte Klasse ohne Parameter:

trait Foo[T] { def foo: T

def bar: String = "Hallo"

}

I Erlauben “Mehrfachvererbung”:

class C extends Foo[Int] with Bar[String] { ... }

I Können auch als Mixins verwendet werden:

trait Funny {

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

(new C with Funny).laugh() // hahaha

(110)

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:

trait Show[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)

(111)

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:

trait Show[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)

Referenzen

ÄHNLICHE DOKUMENTE

I Dynamische Tests führen das Programm unter kontrollierten Bedingungen aus, und prüfen das Ergebnis gegen eine gegebene Spezifikation. I Zentrale Frage: wo kommen die

I Systeme sind eingebettet, nebenläufig, reagieren auf ihre Umwelt.... Warum

I Cold Observables fangen erst an Werte zu produzieren, wenn man ihnen zuhört.. Für jeden Observer

I Aber: zentrales Konzept sind unendliche Listen (Ströme) mit nicht-strikte Auswertung. I Implementation mit Scala-Listen

[r]

I Werte vom Typ IO (Aktionen) können kombiniert werden wie alle anderen. I

I Aber: zentrales Konzept sind unendliche Listen (Ströme) mit nicht-strikte Auswertung. I Implementation mit Scala-Listen

[r]