• Keine Ergebnisse gefunden

Komposition von Zustandsübergängen

N/A
N/A
Protected

Academic year: 2022

Aktie "Komposition von Zustandsübergängen"

Copied!
4
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

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]

(2)

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]

(3)

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]

(4)

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]

Referenzen

ÄHNLICHE DOKUMENTE

I Aber: Haskell Standard-IO blockiert nur den aufrufenden Thread.. IORef a). I Entweder leer oder gefüllt mit Wert vom

I Aber: Haskell Standard-IO blockiert nur den aufrufenden Thread.. IORef a). I Entweder leer oder gef¨ullt mit Wert vom

I Jeder Thread hat einen Identifier: abstrakter Typ ThreadId I Neuen Thread erzeugen: forkIO:: IO()-¿ IO ThreadId I Thread stoppen: killThread :: ThreadId -¿ IO () I

• boolean tryAcquire(int permits, long timeout, TimeUnit unit) permits Freisignale nehmen (blockierend mit Timeout). •

In order to simply return to execution, we must make the process think that code at the address we obtained earlier (the current instruction from the thread context) was a piece

- ajout de quelques gouttes d’une solution de chlorure de baryum : formation d’un précipité blanc. - ajout de quelques gouttes d’une solution de nitrate d’argent : pas

 Communication between user level thread library and kernel in case of many-to-many library and kernel in case of many to many.

Diese meteorologisch optimierten Parametrisierungen werden anschließend in kontrollierter Weise mit einer Inversrechnung derart verändert, daß die resultierenden