Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 20.10.09:
Einf¨uhrung und R¨uckblick
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Organisatorisches
IVorlesung: Di 8 – 10 MZH 7250
I ¨Ubung: Do 10 – 12 MZH 7210 (nach Bedarf) IScheinkriterien:
I 5 ¨Ubungsbl¨atter
I Alle bearbeitet, insgesamt 40% (Notenspiegel PI3)
I ¨Ubungsgruppen 2 – 4 Mitglieder
I Ggf. Fachgespr¨ach am Ende
2
Warum?
IHaskell: Nicht nur f¨ur¨Ubungsaufgaben
IFunktionale Sprachen sindInnovationsinkubatoren
IFunktionale Sprachen behandeln Zukunftsthemenheute
3
Themen
IMonaden undfortgeschrittene Typen
I Was ist eine ”Monade”?
I IOund andere Monaden
I Konstruktorklassen, Rang-2-Polymorphie IFortgeschritteneDatenstrukturen
I Der Zipper
I Str¨ome, Graphen, unendliche Datenstrukturen INebenl¨aufigkeit
I Leichtgewichte Threads in Haskell
I Queues, Semaphoren, Monitore . . .
I State Transactional Memory ITheFutureof Programming
I Dom¨anenspezifische Sprachen (DSLs)
I Fancy Types
I The Next Big Thing: F#, Scala
4
Ressourcen
IHaskell-Webseite:http://www.haskell.org/
IB¨uchereien:
IHaskell 98 Libraries
IHaskell Hierarchical Libraries ICompiler:
IGlasgow Haskell Compiler (ghc) (Version 6.10)
Ihttp://www.haskell.org/ghc/
IB¨ucher und Artikel
ISiehe
http://www.informatik.uni-bremen.de/˜cxl/lehre/asp.ws09/
IAbmorgen!
5
R¨ uckblick Haskell
IDefinition von Funktionen:
I lokale Definitionen mitletundwhere
I Fallunterscheidung undguarded equations
I Abseitsregel
I Funktionen h¨oherer Ordnung ITypen:
I Basisdatentypen:Int,Integer,Rational,Double,Char,Bool
I Strukturierte Datentypen:[a],(a, b)
I Algebraische Datentypem:data Maybe a = Just a — Nothing
6
R¨ uckblick Haskell
IAbstrakte Datentypen
IModule
ITypklassen
IVerz¨ogerte Auswertung und unendliche Datentypen
7
I/O in funktionalen Sprachen
IProblem:Eingabe kann nicht als Funktion r e a d L i n e : : ( ) → S t r i n g
modelliert werden — zerst¨ortreferentielle Transparenz.
IGenerelles Problem hier:Interaktion mit der Umwelt IM¨ogliche L¨osungen:
I Seiteneffekte (e.g. Standard ML);
I Continuations (Auswertungskontext explizit modellieren);
I Streams:readLine :: Instream-¿ (Instream, String)
I Einkapselung inMonaden(Haskell).
8
Monadische I/O
IAbstrakter DatentypIO a:
(=) : : IO t→ ( t→ IO u )→ IO u −−“then”
r e t u r n : : t→ IO t −−“return”
I t :: IO aerst eine Aktion, gibt dann Wert vom Typazur¨uck:
type IO a = World→ ( a , World )
9
Monadische I/O
IElementare Operationen:
g e t L i n e : : IO S t r i n g −−eine Zeile lesen p u t S t r : : S t r i n g→ IO ( ) −−Zeile ausgeben putStrLn : : S t r i n g→ IO ( ) −−Zeile mit LF ausgeben
I“Einmal I/O, immer I/O”
IAbh¨angigkeit von Umwelt am Typ erkennbar
IDaher:
main : : IO ( )
Hauptprogramm hat keinen R¨uckgabewert, nur noch Interaktion.
10
Monadische I/O: Die do Notation
ISyntaktischer Zucker f¨ur Monaden:
echo =
do s ← g e t L i n e g e t L i n e
putStrLn s ←→ =λs → putStrLn s
echo echo
IOder auch:
echo = do { s← g e t L i n e ; putStrLn s ; echo } IN¨utzlich:
() : : IO t→ IO u→ IO u
f g ≡ f =λ → g
11
Monadische I/O: Einfache Beispiele
echo : : IO ( )
echo = g e t L i n e =putStrLn echo
echo = do { l ←g e t L i n e ; putStrLn l ; echo } i n t e r a c t O n c e : : ( S t r i n g→ S t r i n g )→ IO ( ) i n t e r a c t O n c e f = g e t L i n e =( p u t S t r . f )
i n t e r a c t O n c e f = do { l← g e t L i n e ; putStrLn ( f l ) } r e v e ch o : : IO ( )
r e v e ch o = g e t L i n e =putStrLn . r e v e r s e r e v ec h o
r e v e ch o = do { l← g e t L i n e ; putStrLn ( r e v e r s e l ) ; r e v e c h o }
12
File I/O
Abstrakter Zugriff durchlazy evaluation:
type F i l e P a t h = S t r i n g g e t C o n t e n t s : : IO S t r i n g
r e a d F i l e : : F i l e P a t h → IO S t r i n g w r i t e F i l e : : F i l e P a t h → S t r i n g → IO ( ) a p p e n d F i l e : : F i l e P a t h → S t r i n g → IO ( ) Beispiel:
cntWords : : F i l e P a t h → IO ( ) cntWords f i l e = do c← r e a d F i l e f i l e
l e t s = ( l e n g t h . words ) c
p u t S t r $ f i l e++ ” : ”++ show s++ ” wordsλn”
13
Fortgeschrittene File I/O
data IOMode = ReadMode | WriteMode | AppendMode
o p e n F i l e : : F i l e P a t h → IOMode → IO Handle hGetContents : : Handle → IO S t r i n g −−uvm.
hFlush : : Handle → IO ( )
hGetPosn : : Handle → IO HandlePosn
hSetPosn : : HandlePosn → IO ( )
data SeekMode = AbsoluteSeek | R e l a t i v e S e e k | SeekFromEnd
hSeek : : Handle → SeekMode → I n t e g e r → IO ( )
Weitere ¨ubliche Operationen (Buffering etc) sieheHaskell98 Library Report, Kap. 11.
14
Fehler!
Repr¨asentation durch den abstrakten DatentypIOError. Ausnahmebehandlung ¨ahnlich in Java:
i o E r r o r : : I O E r r o r → IO a
catch : : IO a → ( I O E r r o r → IO a )→ IO a Beispiel:
cntW f i l e = catch ( cntWords f i l e )
(λe→ p u t S t r ( ” E r r o r : ” ++ ( show e ) ) ) Analyse der Fehler durchisDoesNotExistsError :: IOError-¿ Bool
etc.
Kommandozeilenargumente
Interaktion mit der Umgebung: ModulSystem
data ExitCode = E x i t S u c c e s s | E x i t F a i l u r e I n t getArgs : : IO [ S t r i n g ]
getProgName : : IO S t r i n g
getEnv : : S t r i n g → IO S t r i n g
system : : S t r i n g → IO ExitCode
e x i t W i t h : : ExitCode → IO a Beispiel:
main =do r←getProgName ; a← getArgs catch (mapM cntWords a )
(λe→ putStrLn ( r++ ” : ”++ ( show e ) ) )
Das Modul Directory
c r e a t e D i r e c t o r y : : F i l e P a t h → IO ( ) removeDirectory , r e m o v e F i l e : : F i l e P a t h → IO ( ) renameDirectory , r e n a m e F i l e : : F i l e P a t h → F i l e P a t h → IO ( )
g e t D i r e c t o r y C o n t e n t s : : F i l e P a t h → IO [ F i l e P a t h ] g e t C u r r e n t D i r e c t o r y : : IO F i l e P a t h
s e t C u r r e n t D i r e c t o r y : : F i l e P a t h → IO ( ) data P e r m i s s i o n s = . . .
r e a d a b l e , w r i t e a b l e , e x e c u t a b l e , s e a r c h a b l e : : P e r m i s s i o n s→ Bool g e t P e r m i s s i o n s : : F i l e P a t h → IO P e r m i s s i o n s
s e t P e r m i s s i o n s : : F i l e P a t h → P e r m i s s i o n s → IO ( ) g e t M o d i f i c a t i o n T i m e : : F i l e P a t h → IO ClockTime
17
Das Modul Directory, Beispiel
import D i r e c t o r y import Time
import System ( getArgs ) c l e a nu p d i r =
do now←getClockTime
c← g e t D i r e c t o r y C o n t e n t s d i r s e t C u r r e n t D i r e c t o r y d i r
mapM (λf→ do {mt← g e t M o d i f i c a t i o n T i m e f ; i f ( ( l a s t f ≡ ’ ˜ ’ ) &&
tdDay ( d i f f C l o c k T i m e s mt now ) ≥ 1)
then r e m o v e F i l e f e l s e r e t u r n ( )}) c main =do { d← getArgs ; c l ea n u p ( head d ) }
18
Systemfunktionen f¨ ur Haskell
IAbstrakte Modellierung inHaskell98 Standard Library:
IO,Directory System,Time SieheLibrary Report
IKonkrete Modellierung in ModulPosix(nur f¨urGHC) nach IEEE Standard 1003.1, e.g.:
e x e c u t e F i l e : : F i l e P a t h −−Command
→ Bool −−Search
PATH?
→ [ S t r i n g ] −−Arguments
→ Maybe [ ( S t r i n g , S t r i n g ) ] −−Environ- ment
→ IO ( )
19
More IO
N¨utzliche Kombinatoren (aus demPrelude):
sequence : : [ IO a ] → IO [ a ] s e q u e n c e : : [ IO a ] → IO ( )
mapM : : ( a → IO b ) → [ a ] → IO [ b ]
mapM : : ( a → IO b ) → [ a ] → IO ( )
Mehr im ModulMonad(Library Report, Kapt. 10).
20
Zusammenfassung
IAbh¨angigkeit von Aussenwelt in TypIOkenntlich
IBenutzung von IO: vordefinierte Funktionen in der Haskell98 B¨ucherei
IN¨achstes Mal:
IWas steckt dahinter?
IFlucht aus Alcatraz – IO f¨ur Erwachsene
IEndlich Variablen
21
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 27.10.09:
Monads — The Inside Story
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Heute in diesem Theater
IDie Geheimnisse der Monaden
IEndlich Zuweisungen
IFlucht aus Alcatraz –IOf¨ur Erwachsene
2
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen
IEinf¨uhrung, Wiederholung
IZust¨ande, Zustands¨uberg¨ange undIO
IReader/Writer, Nichtdeterminismus, Ausnahmen
IMonadentransformer
ITeil II: Fortgeschrittene Datenstrukturen ITeil III: Nebenl¨aufigkeit
ITeil IV: The Future of Programming
3
Zustands¨ ubergangsmonaden
IAktionen (IO a) sind keine schwarze Magie.
IGrundprinzip: SystemzustandΣwird explizit behandelt.
f :: a→IO b ∼= f :: (a, Σ)→(b,Σ) FolgendeInvariantenm¨ussen gelten:
I Systemzustand darfnie dupliziertodervergessenwerden.
I Auswertungsreihenfolge muß erhalten bleiben.
IKompositionmussInvariantenerhalten Zustands¨ubergangsmonaden
4
Zustands¨ ubergangsmonaden
ITyp:
type ST s a = s → ( a , s )
a → ST s b=a→s→(b, s)∼=(a, s)→(b, s) Parametrisiert ¨uber Zustandsund Berechnungswerta.
IKomposition durch
(=) : : ST s a→ ( a→ ST s b )→ ST s b
5
Komposition von Zustands¨ uberg¨ angen
IIm Prinzip Vorw¨artskomposition:
(=) : : 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 ) )
IDamitf =g = uncurry g . f.
IAber:STkannkein Typsynonymsein
IN¨otig:abstrakter DatentypumInvariantenzu erhalten
6
ST als Abstrakter Datentyp
IDatentyp verkapseln:
newtype ST s a = ST ( s→ ( a , s ) ) IHilfsfunktion (Selektor)
unwrap : : ST s a→ ( s→ ( a , s ) ) unwrap (ST f ) = f
IDamit ergibt sich
f =g = ST ( u n c u r r y ( unwrap . g ) . unwrap f ) r e t u r n a = ST (λs→ ( a , s ) )
Aktionen
IAktionen: Zustandstransformationen auf der Welt ITypRealWorld#repr¨asentiert Außenwelt
I Typ hat genau einen Wertrealworld #, der nur f¨ur initialen Aufruf erzeugt wird.
I Aktionen:typeIO a = ST RealWorld# a IOptimierungen:
I ST s adurchin-place-updateimplementieren.
I IO-Aktionen durcheinfachen Aufrufersetzen.
I Compiler darf keine Redexe duplizieren!
I TypIOstelltlediglichReihenfolge sicher.
Was ist eigentlich eine Monade?
ISTmodelliertimperative Konzepte.
IBeobachtung:Andere Konzepte k¨onnen¨ahnlich modelliertwerden:
IAusnahmen:f :: a-¿ Maybe bmit Komposition (=) : : Maybe a→ ( a→ Maybe b )→ Maybe b J u s t a =f = f a
Nothing=f = Nothing
IBen¨otigen Typklassen f¨ur Typkonstruktoren. . .
9
Konstruktorklassen
IKonstruktorklassen: Typklassen f¨ur Typkonstruktoren (kinds) IBeispiel:
c l a s s Functor f where
fmap : : ( a→ b ) → ( f a → f b )
i n s t a n c e Functor [ ] where −−Kein ’echtes’ Haskell!
fmap f [] = []
fmap f ( x : xs ) = f x : map f xs IErweiterungdes Typsystems (bleibtentscheidbar) IF¨ur ZustandstransformerST:
i n s t a n c e Monad (ST s ) where
f =g = ST ( u n c u r r y ( unwrap . g ) . unwrap f ) r e t u r n a = ST (λs→ ( a , s ) )
10
Monads: The Inside Story
c l a s s Monad m where
(=) : : m a → ( a → m b ) → m b r e t u r n : : a → m a
() : : m a → m b → m b f a i l : : S t r i n g → m a pq = p=λ → q
f a i l s = e r r o r s
FolgendeGleichungenm¨ussen (sollten)gelten:
return a=k = k a m=return = m
m=(λx→k x=h) = (m=k)=h
11
Beispiel: Speicher und Referenzen
ISignatur:
type Mem a
i n s t a n c e Mem Monad IReferenzen sind abstrakt:
type Ref
newRef : : Mem Ref
ISpeicher liest/schreibtString:
readRef : : Ref → Mem S t r i n g w r i t e R e f : : Ref → S t r i n g→ Mem ( )
12
Implementation der Referenzen
Speicher: Liste von Strings, Referenzen: Index in Liste.
type Mem = ST [ S t r i n g ] −−Zustand type Ref = I n t
newRef = ST (λs→ ( l e n g t h s , s++[ ”” ] ) ) readRef r = ST (λs→ ( s ! ! r , s ) ) w r i t e R e f r v = ST (λs→ ( ( ) ,
take r s ++ [ v ]++ drop ( r +1) s ) ) run : : Mem a→ a
run (ST f )= f s t ( f [] )
13
IORef — Referenzen
IDatentyp der Standardb¨ucherei (GHC, Hugs)
import Data . IORef data IORef a
newIORef : : a → IO ( IORef a ) readIORef : : IORef a → IO a w r i t e I O R e f : : IORef a → a → IO ( )
modifyIORef : : IORef a → ( a → a ) → IO ( )
atomicModifyIORef : : IORef a → ( a → ( a , b ) ) → IO b
IImplementation: “echte” Referenzen.
14
Beispiel: Referenzen
f a c : : I n t→ IO I n t
f a c x = do acc ←newIORef 1 loop acc x where
loop acc 0 = readIORef acc loop acc n = do t ← readIORef acc
w r i t e I O R e f acc ( t∗ n ) loop acc ( n−1)
15
Flucht aus Alcatraz
IAus demIO-Monaden gibt es keinen Ausweg.
I Im Gegensatz zu z.B.Maybe:
fromMaybe : : a → Maybe a → a
IDas ist manchmal unpraktisch: Initialisierungen etc.
IF¨urSTgibt es
fixST : : ( a → ST s a ) → ST s a −−Fixpunkt runST : : (f o r a l l s . ST s a ) → a −−NB: Typ!
IF¨urIOgibt es . . .
16
Unsichere Aktionen
ISignatur:
import System . IO . Unsafe ( unsafePerformIO ) unsafePerformIO : : IO a → a
IWarnung: gef¨ahrlichund nichttypsicher!
t e s t : : IORef [ a ]
t e s t = unsafePerformIO $ newIORef []
main = do w r i t e I O R e f t e s t [ 4 2 ] bang←readIORef t e s t putStrLn ( bang : : [ Char ] )
17
Verwendung von unsafePerformIO
IIO-Aktionen, die nur
I einmaldurchgef¨uhrt werden sollen, und
I von anderen IO-Aktionenunabh¨angigsind
I Beispiel: Konfigurationsdatei lesen
IAlloziierungglobaler Ressourcen(z.B. Referenzen).
IDebugging(traces, logfiles).
IEnjoyresponsibly!
18
Benutzung von unsafePerformIO
IAlloziierung globaler Referenzen:
−−— Generate a new identifier.
newId : : IO I n t
newId = atomicModifyIORef r $ λi→ ( i +1, i ) where r = unsafePerformIO $ newIORef 1 {−# NOINLINE newId #−}
INOINLINEbeachten — Optimierungen verhindern.
IDebugging:
t r a c e : : S t r i n g → a→ a
t r a c e s x = unsafePerformIO $ putStrLn s r e t u r n x
ISchon vordefiniert (Debug.Trace).
19
Zusammenfassung & Ausblick
IBlick hinter die Kulissen vonIO IMonaden und andere Kuriosit¨aten
IReferenzen
IunsafePerformIO
IN¨achstes Mal: Mehr Monaden. . .
20
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 03.11.09:
Mehr ¨uber Monaden
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Heute gibt’s:
IMonaden, mehrMonaden, und noch mehrMonaden
I Die Monaden der Standardb¨ucherei
I Referenz:http://www.haskell.org/all˙about˙monads/html
2
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen
IEinf¨uhrung, Wiederholung
IZust¨ande, Zustands¨uberg¨ange undIO
IReader/Writer, Nichtdeterminismus, Ausnahmen
IMonadentransformer
ITeil II: Fortgeschrittene Datenstrukturen ITeil III: Nebenl¨aufigkeit
ITeil IV: The Future of Programming
3
Die Identit¨ atsmonade
IDie allereinfachste Monade:
type I d a = a
i n s t a n c e Monad I d where r e t u r n a = a
b=f = f b
4
Fehlermonaden
IErste N¨ahrung:Maybe
IMaybekennt nurNothing, daher strukturierte Fehler:
data E i t h e r a b = L e f t a | Right b type E r r o r a = E i t h e r S t r i n g a i n s t a n c e Monad ( E i t h e r S t r i n g ) where
( Right a )=f = f a ( L e f t l ) =f = L e f t l r e t u r n b = Right b
INachteil: Fester Fehlertyp IL¨osung: Typklassen
5
Die Monade Control.Monad.Error
ITypklasseErrorf¨ur Fehler c l a s s E r r o r a where
noMsg : : a
strMsg : : S t r i n g → a IFehlermonade parametrisiert ¨ubere:
c l a s s (Monad m) ⇒ MonadError e m where t h r o w E r r o r : : e → m a
c a t c h E r r o r : : m a → ( e → m a ) → m a
i n s t a n c e MonadError ( E i t h e r e ) where t h r o w E r r o r = L e f t
( L e f t e ) ‘ c a t c h E r r o r ‘ h a n d l e r = h a n d l e r e a ‘ c a t c h E r r o r ‘ = a
6
Die Zustandsmonade
I
IZustands¨ubergang als Funktion
newtype S t a t e s a = S t a t e {unwrap : : ( s → ( a , s ) )} i n s t a n c e Monad ( S t a t e s ) where
r e t u r n a = S t a t e $ λs → ( a , s )
( S t a t e g )= f = S t a t e ( u n c u r r y g . unwrap f )
INachteil 1:Zustands¨ubergangnicht-strikt(insbesonderelazy)!
IL¨osung:Strikter Zustands¨ubergang—Control.Monad.ST INachteil 2:Zustands¨ubergang≡Funktion
IL¨osung:Typklassen
7
Die Monad Control.Monad.State
ITypklasseStatef¨ur Zustand lesen/schreiben:
c l a s s MonadState m s where get : : m s
put : : s → m ( )
IZustandsmonade parametrisiert ¨uberState:
i n s t a n c e MonadState ( S t a t e s ) s where get = S t a t e $ λs → ( s , s ) put s = S t a t e $ λ → ( ( ) , s )
IAber: manchmalliestman nur, manchmalschreibtman nur. . .
8
Die Lesemonade
IIntuition: Werte der Eingabeelesen und verabeiten.
ILese-Teil der Zustandsmonade
newtype Reader e a = Reader ( e → a ) i n s t a n c e Monad ( Reader e ) where
r e t u r n a = Reader $ λe → a ( Reader r )=f = Reader $
λe → l e t Reader g = f ( r e ) i n g e
IEingabe wirdnichtmodifiziert.
IBeispiel: Lesen ausSymboltabelle(Gegenbeispiel: Datei)
9
Die Monade Control.Monad.Reader
IWie vorher: Abstraktion derLeseoperationen
INeu:LokalerZustand
c l a s s MonadReader e m where ask : : m e
l o c a l : : ( e → e ) → m a → m a i n s t a n c e MonadReader ( Reader e ) where
ask = Reader i d
l o c a l f c = Reader $ λe → runReader c ( f e ) a s k s : : ( MonadReader e m) ⇒ ( e → a ) → m a a s k s s e l = ask = r e t u r n . s e l
10
Die Schreibmonade
IProduziert einen Strom von Werten IKein Zugriff auf geschriebene Werte m¨oglich
IBeispiel: “Logging”
newtype W r i t e r w a = W r i t e r ( a , [w ] ) i n s t a n c e Monad ( W r i t e r w) where
r e t u r n a = W r i t e r ( a , [] )
( W r i t e r ( a ,w) )=f = l e t W r i t e r ( a ’ , w’ ) = f a i n W r i t e r ( a ’ , w++ w’ ) IAbstraktion: auch ¨uberListenvon Ausgabewerten
11
Die Monade Control.Monad.Writer
ITypklasseMonoid: Verallgemeinerte Listen
c l a s s ( Monoid w, Monad m) ⇒ MonadWriter w m where pass : : m ( a , w → w) → m a
l i s t e n : : m a → m ( a , w) t e l l : : w → m ( )
i n s t a n c e MonadWriter ( W r i t e r w) where pass ( W r i t e r ( ( a , f ) ,w) ) = W r i t e r ( a , f w) l i s t e n ( W r i t e r ( a ,w) ) = W r i t e r ( ( a ,w) ,w)
t e l l s = W r i t e r ( ( ) , s )
l i s t e n s : : ( MonadWriter w m) ⇒ (w → w) → m a → m ( a ,w) l i s t e n s f m = do ( a ,w)←m; r e t u r n ( a , f w) c e n s o r : : ( MonadWriter w m) ⇒
(w → w) → m a → m a
c e n s o r f m = pass $ do a←m; r e t u r n ( a , f )
12
Die Listenmonade
IListen sind Monaden:
i n s t a n c e Monad [] where
m=f = concatMap f m
r e t u r n x = [ x ] f a i l s = []
IIntuition:f :: a→[b]Liste der m¨oglichen Resultate
IReihenfolge der M¨oglichkeiten relevant?
13
Der Monade Set
IData.Setsind Monaden:
i n s t a n c e Monad Set where
m=f = Set . u n i o n s ( Set . map f m) r e t u r n x = Set . s i n g l e t o n x
f a i l s = Set . empty
INichtvordefiniert . . .
14
Der Continuationmonade
IAuswertungskontext wird explizit modelliert.
newtype Cont r a =
Cont { runCont : : ( ( a → r ) → r ) }
Irist der Typ der gesamten Berechnung Ia→rist der momentane Kontext
i n s t a n c e Monad ( Cont r ) where r e t u r n a = Cont $ λk → k a ( Cont c )=f = Cont $
λk → c (λa → runCont ( f a ) k )
Control.Monad.Cont
IcallCC: GOTO f¨ur funktionale Sprachen
c l a s s (Monad m) ⇒ MonadCont m where c a l l C C : : ( ( a → m b ) → m a ) → m a i n s t a n c e MonadCont ( Cont r ) where
c a l l C C f = Cont $
λk → runCont ( f (λa → Cont $ λ → k a ) ) k
ILiebernichtbenutzen!
Exkurs: Was ist eigentliche eine Monade?
IMonade: Konstrukt ausKategorientheorie IMonade∼=(verallgemeinerter) Monoid IMonade: gegeben durchalgebraische Theorien
IOperationen endlicher (beschr¨ankter) Aritit¨at
IGleichungen
IBeispiele:Maybe,List,Set,State, . . . IMonaden in Haskell:computational monads
IStrukturierte Notation f¨urBerechnungsparadigmen
IBeispiel: Rechner mit Fehler, Nichtdeterminismus, Zustand, . . .
17
Kombination von Monaden: Das Problem
IGegeben zweiMonaden:
c l a s s Monad m1 where . . . c l a s s Monad m2 where . . .
IEs gelten weder
i n s t a n c e Monad (m1 (m2 a ) ) i n s t a n c e Monad (m2 (m1 a ) ) IProblem:Monadengesetzegelten nicht.
IL¨osung:N¨achsteVorlesung
18
Zusammenfassung
IMonaden sind praktischeAbstraktion IWir haben kennengelernt:
IFehlermonaden:Maybe,Either,MonadError
IZustandsmonaden:State,ST,IO
ILese/Schreibmonade:ReaderMonad,WriterMonad
INichtdeterminusmus:[a],Data.Set
IExplizite Spr¨unge:Continuation
IWichtigesStrukturierungsmittelf¨ur funktionale Programme IKombination bereitet (noch) Probleme . . .
19
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 10.11.09:
Monadentransformer
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Heute gibt’s:
IEine Monade istgut, mehrere Monaden sindbesser
IKombination von Monaden
IMonadentransformer
2
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen
IEinf¨uhrung, Wiederholung
IZust¨ande, Zustands¨uberg¨ange undIO
IReader/Writer, Nichtdeterminismus, Ausnahmen
IMonadentransformer
ITeil II: Fortgeschrittene Datenstrukturen ITeil III: Nebenl¨aufigkeit
ITeil IV: The Future of Programming
3
Kombination von Monaden: Das Problem
IGegeben zweiMonaden:
c l a s s Monad m1 where . . . c l a s s Monad m2 where . . .
IEs gelten weder
i n s t a n c e Monad (m1 (m2 a ) ) i n s t a n c e Monad (m2 (m1 a ) )
IProblem:Monadengesetzegelten nicht.
4
Monadentransformer
IMonadentransformer:
IErweiterbare Monade
IMonade mitLoch newtypeM a = . . . i n s t a n c e Monad M where
f =g = . . . r e t u r n x = . . .
⇒
newtype M m a = . . .
i n s t a n c e Monad m⇒ Monad M m where f =g = . . .
r e t u r n x = . . .
5
Monadentransformer: Beispiele
1.Beispiel:Zustandsmonadentransformer type S t a t e T s m a = s→ m ( s , a )
I Zustandsbasierte Berechnungenin einer anderen Monadem
I StateT s Identity ist Zustandsmonade
2.Beispiel:Fehlermonadentransformer
type E r r o r T e m a = m ( E i t h e r e a )
I Fehlerbehaftete Berechnungenin einer anderen Monadem
I ErrorT e Identity ist Fehlermonade
6
Reihenfolge beachten!
IKombination vonStateundError IErstZustand, dannFehler:
type E r r o r S t a t e s a =
E r r o r T ( S t a t e T s I d ) = E i t h e r S t r i n g ( s→ ( s , a ) )
IBerechungm :: ErrorState s a: Fehler oder zustandsbehaftet
IE.g. Fehler in Haskell
IErstFehler, dannZustand:
type S t a t e E r r o r s a = s→ ( s , E i t h e r S t r i n g a )
IBerechungm :: StateError s a: Immer zustandsbehaftet, Resultat Fehler oder normal
IE.g. Fehler in imperativen Sprachen
Standardtransformer
Standard-Monade Transformer Standard-Typ Transformierter Typ
Error ErrorT Either e a m (Either e a)
State StateT s →(a, s) s →m (a,s)
Reader ReaderT r →a r→m a
Writer WriterT (a,w) m (a,w)
Cont ContT (a→r)→r (a→m r)→m r
Quelle:http://www.haskell.org/all˙about˙monads/
Fallbeispiel: ein Modularer Interpreter
IZiel: Interpreter f¨ur eine einfache imperative Sprache IModularer Aufbau:
1.Nur Zustand
2.Ausgabe
3.Eingabe
4.Fehler und Fehlerbehandlung
9
Zusammenfassung
IWarumMonaden kombinieren?
I Typ definiertEffekt
I E.g.WriterT— nur Logging, kein Zustand
I Pragmatisch: dieIO-Monade (imperativ)
IVorteileMonadentransfomer:
I Erlauben modulare Kombination von Monaden
I Standard-B¨ucherei (Monad Template Library) bietet Standard-Monaden als praktischen Bausatz
INachteileMonadentransformer:
I F¨ur neue TransformerKombinationmitallenanderen zu bedenken!
I Nichtkompositional
IN¨achste Woche:?
10
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 17.11.09:
Der Zipper
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen
ITeil II: Fortgeschrittene Datenstrukturen
I Der Zipper
I Str¨ome, Graphen, unendliche Datenstrukturen
ITeil III: Nebenl¨aufigkeit
ITeil IV: The Future of Programming
2
Das Problem
IFunktional = kein Zustand
IWiedestruktiver Update?
IManipulationinnerhalbeiner Datenstruktur
IBeispiel: ZeilenorientierterEditor, Abstrakte Syntax
IL¨osung: DerZipper
IKeine feste Datenstruktur, sondern einSchema
3
Ein einfacher Editor
IDatenstrukturen:
type Text = [ S t r i n g ]
data Pos = Pos { l i n e : : Int , c o l : : I n t} data E d i t o r = Ed { t e x t : : Text
, c u r s o r : : Pos }
IOperationen: Cursorbewegen(links) g o l e f t : : E d i t o r → E d i t o r g o l e f t Ed{t e x t= t , c u r s o r= c}
| c o l c ≡ 0 = e r r o r ”At s t a r t o f l i n e ”
| o t h e r w i s e =
Ed{t e x t= t , c u r s o r=c{c o l= c o l c− 1}}
4
Beispieloperationen
ITextrechtseinf¨ugen:
i n s e r t r i g h t : : E d i t o r→ S t r i n g→ E d i t o r i n s e r t r i g h t Ed{t e x t= t , c u r s o r= c} t e x t =
l e t ( as , bs ) = s p l i t A t ( c o l c ) ( t ! ! l i n e c ) i n Ed{t e x t= updateAt ( l i n e c ) t
( as ++ t e x t++ bs ) , c u r s o r= c}
updateAt : : I n t→ [ a ]→ a→ [ a ] updateAt n as a = c a s e s p l i t A t n as o f
( bs , [] ) → e r r o r ” updateAt : l i s t too s h o r t . ” ( bs , : cs ) → bs ++ a : cs
IProblem:Aufwandf¨ur Manipulation
5
Manipulation strukturierter Datentypen
IAnderes Beispiel:n-¨are B¨aume(rose trees) data Tree a = Leaf a
| Node [ Tree a ] d e r i v i n g Show
I Bsp: Abstrakte Syntax von einfachen Ausdr¨ucken
IUpdateauf Beispieltermt=a∗b−c∗d: ersetzebdurchx+y t = Node [ Leaf ”−”
, Node [ Leaf ”∗” , Leaf ”a” , Leaf ”b” ] , Node [ Leaf ”∗” , Leaf ” c ” , Leaf ”d” ] ]
6
Der Zipper
IIdee:Kontextnichtwegwerfen!
INicht:typePath = [Int]
ISondern:
data Ctxt a = Empty
| Cons [ Tree a ] ( Ctxt a ) [ Tree a ]
IKontext ist ‘inverse Umgebung’ (“Like a glove turned inside out”)
ILoc aistBaummitFokus
newtype Loc a = Loc ( Tree a , Ctxt a )
IWarumnewtype?
Zipping Trees: Navigation
IFokus nachlinks
g o l e f t : : Loc a→ Loc a g o l e f t ( Loc ( t , c ) ) = c a s e c o f
Empty → e r r o r ” g o l e f t at empty ”
Cons ( l : l e ) up r i → Loc ( l , Cons l e up ( t : r i ) ) Cons [] → e r r o r ” g o l e f t o f f i r s t ” IFokus nachrechts
g o r i g h t : : Loc a→ Loc a g o r i g h t ( Loc ( t , c ) ) = c a s e c o f
Empty → e r r o r ” g o r i g h t at empty ”
Cons l e up ( r : r i ) → Loc ( r , Cons ( t : l e ) up r i ) Cons [] → e r r o r ” g o r i g h t o f l a s t ”
Zipping Trees: Navigation
IFokus nachoben
go up : : Loc a→ Loc a
go up ( Loc ( t , c ) ) = c a s e c o f Empty → e r r o r ” go up o f empty ” Cons l e up r i →
Loc ( Node ( r e v e r s e l e ++ t : r i ) , up ) IFokus nachunten
go down : : Loc a→ Loc a
go down ( Loc ( t , c ) ) = c a s e t o f Leaf → e r r o r ” go down at l e a f ” Node [] → e r r o r ” go down at empty ” Node ( t : t s ) → Loc ( t , Cons [] c t s )
9
Zipping Trees: Navigation
IHilfsfunktion:
top : : Tree a→ Loc a top t = ( Loc ( t , Empty ) )
IDamit andere Navigationsfunktionen:
path : : Loc a→ [ I n t ]→ Loc a path l [] = l
path l ( i : ps )
| i ≡ 0 = path ( go down l ) ps
| i > 0 = path ( g o l e f t l ) ( i−1) ps
10
Einf¨ ugen
IEinf¨ugen: Wo?
ILinksdes Fokus einf¨ugen
i n s e r t l e f t : : Tree a→ Loc a→ Loc a i n s e r t l e f t t1 ( Loc ( t , c ) ) = c a s e c o f
Empty → e r r o r ” i n s e r t l e f t : i n s e r t at empty ” Cons l e up r i → Loc ( t , Cons ( t1 : l e ) up r i ) IRechtsdes Fokus einf¨ugen
i n s e r t r i g h t : : Tree a→ Loc a→ Loc a i n s e r t r i g h t t1 ( Loc ( t , c ) ) = c a s e c o f
Empty → e r r o r ” i n s e r t r i g h t : i n s e r t at empty ” Cons l e up r i → Loc ( t , Cons l e up ( t1 : r i ) ) IUnterhalbdes Fokus einf¨ugen
i n s e r t d o w n : : Tree a→ Loc a→ Loc a i n s e r t d o w n t1 ( Loc ( t , c ) ) = c a s e t o f
Leaf → e r r o r ” i n s e r t d o w n : i n s e r t at l e a f ” Node t s → Loc ( t1 , Cons [] c t s )
11
Ersetzen und L¨ oschen
IUnterbaum im Fokusersetzen:
update : : Tree a→ Loc a→ Loc a update t ( Loc ( , c ) ) = Loc ( t , c ) IUnterbaum im Fokus l¨oschen: wo ist der neue Fokus?
1. RechterBaum, wenn vorhanden 2. LinkerBaum, wenn vorhanden 3. Elternknoten
d e l e t e : : Loc a→ Loc a d e l e t e ( Loc ( , p ) ) = c a s e p o f
Empty → Loc ( Node [] , Empty )
Cons l e up ( r : r i ) → Loc ( r , Cons l e up r i ) Cons ( l : l e ) up [] → Loc ( l , Cons l e up [] ) Cons [] up [] → Loc ( Node [] , up )
I “We note thatdeleteis not such a simple operation.”
12
Schnelligkeit
IWieschnellsind Operationen?
IAufwand: go leftO(left(n)), alle anderenO(1).
IWarumsind Operationen so schnell?
IKontext bleibterhalten
IManipulation: reineZeiger-Manipulation
13
Der Zipper als Monade
ILocMist Zustandsmonade mit ZustandLoc
newtype LocM a b = LocM {l o c S t : : S t a t e ( Loc a ) b}
d e r i v i n g ( Functor , Monad , MonadState ( Loc a ) ) IStartfunktion
run : : LocM a b→ Tree a→ ( b , Loc a ) run l t = r u n S t a t e ( l o c S t l ) ( top t ) IZugriff auf Baum imFokus
c u r r e n t : : LocM a ( Tree a )
c u r r e n t = do Loc ( l , c )← get ; r e t u r n l INavigation
l e f t : : LocM a ( ) l e f t = modify g o l e f t
−−etc.
14
Zipper Monad: Manipulation
IL¨oschen:
d e l : : LocM a ( ) d e l = modify d e l e t e IUpdate:
upd : : Tree a→ LocM a ( ) upd t = modify ( update t ) IEinf¨ugen (bsp rechts):
i n s r : : Tree a→ LocM a ( ) i n s r t = modify ( i n s e r t r i g h t t ) IHilfspr¨adikat:rightm¨oglich?
h a s r i g h t : : Loc a → Bool h a s r i g h t ( Loc ( l , c ) ) = c a s e c o f
( Cons ( : ) ) → True ; → F a l s e h a s r i : : LocM a Bool
h a s r i = g e t s h a s r i g h t 15
Zipper f¨ ur andere Datenstrukturen
IBin¨are B¨aume:
data Tree a = Leaf a | Node ( Tree a ) ( Tree a )
IKontext:
data Ctxt a = Empty
| Le ( Ctxt a ) ( Tree a )
| Ri ( Tree a ) ( Ctxt a ) newtype Loc a = Loc ( Tree a , Ctxt a )
16
Tree-Zipper: Navigation
IFokus nachlinks
g o l e f t : : Loc a→ Loc a
g o l e f t ( Loc ( t , c t x ) ) = c a s e c t x o f Empty → e r r o r ” g o l e f t at empty ” Le c r → e r r o r ” g o l e f t o f l e f t ” Ri l c → Loc ( l , Le c t )
IFokus nachrechts
g o r i g h t : : Loc a→ Loc a
g o r i g h t ( Loc ( t , c t x ) ) = c a s e c t x o f Empty → e r r o r ” g o r i g h t at empty ” Le c r → Loc ( r , Ri t c )
Ri → e r r o r ” g o r i g h t o f r i g h t ”
17
Tree-Zipper: Navigation
IFokus nachoben
go up : : Loc a→ Loc a
go up ( Loc ( t , c t x ) ) = c a s e c t x o f Empty → e r r o r ” go up o f empty ” Le c r → Loc ( Node t r , c ) Ri l c → Loc ( Node l t , c ) IFokus nachunten links
g o d o w n l e f t : : Loc a→ Loc a g o d o w n l e f t ( Loc ( t , c ) ) = c a s e t o f
Leaf → e r r o r ” go down at l e a f ” Node l r → Loc ( l , Le c r )
IFokus nachunten rechts
g o d o w n r i g h t : : Loc a→ Loc a g o d o w n r i g h t ( Loc ( t , c ) ) = c a s e t o f
Leaf → e r r o r ” go down at l e a f ” Node l r → Loc ( r , Ri l c )
18
Tree-Zipper: Einf¨ ugen und L¨ oschen
IEinf¨ugenlinks
i n s l e f t : : Tree a→ Loc a→ Loc a
i n s l e f t t1 ( Loc ( t , c t x ) ) = Loc ( t , Ri t1 c t x ) IEinf¨ugenrechts
i n s r i g h t : : Tree a→ Loc a→ Loc a
i n s r i g h t t1 ( Loc ( t , c t x ) ) = Loc ( t , Le c t x t1 ) IL¨oschen
d e l e t e : : Loc a→ Loc a d e l e t e ( Loc ( , c ) ) = c a s e c o f
Empty → e r r o r ” d e l e t e o f empty ” Le c r → Loc ( r , c )
Ri l c → Loc ( l , c )
INeuer Fokus: anderer Teilbaum
19
Tree-Zipper: Variation
IBin¨are B¨aume, Werte imKnoten:
data Tree a = N i l | Node ( Tree a ) a ( Tree a )
IKontext enth¨altKnotenwert data Ctxt a = Empty
| Le ( Ctxt a ) a ( Tree a )
| Ri ( Tree a ) a ( Ctxt a )
IFunktionen ¨ahnlich, aber:
I deletetotal, l¨oschtKnoteninhaltim Kontext
20
Zipping Lists
IListen:
data L i s t a = N i l | Cons a ( L i s t a )
IDamit:
data Ctxt a = Empty | Snoc ( Ctxt a ) a
IListen sind ihr ‘eigener Kontext’ : List a∼=Ctxt a
21
Zipping Lists: Fast Reverse
IListenumkehrschnell:
f a s t r e v : : [ a ]→ [ a ] f a s t r e v xs = r e v xs []
r e v : : [ a ]→ [ a ]→ [ a ] r e v [] as = as
r e v ( x : xs ) as = r e v xs ( x : as )
IZweites Argument vonrev:Kontext
I Liste der Elemente davor inumgekehrterReihenfolge
22
Zusammenfassung
IDerZipper
IManipulation von Datenstrukturen
IZipper = Kontext + Fokus
IEffiziente destruktive Manipulation INachteile
INicht richtig generisch —Schema, keine B¨ucherei
IViel schematischer Code f¨ur jeden Datentyp
IAbhilfe:Generic Zipper
IN¨achstes Mal: Graphen, Str¨ome, unendliche Datenstrukturen
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 24.11.09:
Unendliche Datentypen und Graphen
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen
ITeil II: Fortgeschrittene Datenstrukturen
I Der Zipper
I Str¨ome, Graphen, unendliche Datenstrukturen
ITeil III: Nebenl¨aufigkeit
ITeil IV: The Future of Programming
2
Das Tagesmen¨ u
IReprise:Str¨ome(uendliche Listen)
IDoppeltverketteteListen
IGraphen
3
Unendliche Datenstrukturen: Str¨ ome
IStr¨ome: Unendliche Listen
data Stream a = Stream { hd : : a , t l : : Stream a }
IObservatoren:
hd : : Stream a→ a t l : : Stream a→ Stream a
4
Bsp: Fibonacci-Zahlen
IFibonacci-Zahlen alsStrom
ISeifibs :: [Integer]Strom aller Fib’zahlen:
f i b s 1 1 2 3 5 8 13 21 34 55 t a i l f i b s 1 2 3 5 8 13 21 34 55 t a i l ( t a i l f i b s ) 2 3 5 8 13 21 34 55 IDamit ergibt sich:
f i b s : : Stream I n t e g e r
f i b s = Stream 1 ( Stream 1 $ z i pS (+) f i b s ( t l f i b s ) ) In-te Fibonaccizahl mitith n fibs
IAufwand:linear, dafibsnur einmal ausgewertet wird.
5
Doppelt Verkettete Listen
IIn Haskell wie in Java/C: Zeiger aufVorg¨anger,Nachfolger data DList a = DLNode { prev : : DList a
, v a l : : a
, next : : DList a }
| DLEmpty
I deriving(Eq, Show)???
IKeinfreierDatentyp: es geltenInvarianten d /= DLEmpty && next d /= DLEmpty
⇒ prev ( next d)= d d /= DLEmpty && prev d /= DLEmpty
⇒ next ( prev d)= d
6
Listen erzeugen
IDoppelt verkettete Listen erzeugen:
f r o m L i s t : : [ a ]→ DList a f r o m L i s t as = mkDList DLEmpty as mkDList : : DList a→ [ a ]→ DList a mkDList c u r r p r e v [] = DLEmpty mkDList c u r r p r e v ( x : xs ) =
l e t he r e = DLNode c u r r p r e v x ( mkDList h er e xs ) i n he r e
IProblem: Knoten einf¨ugen/l¨oschen
IEinfachefalscheL¨osung
IRichtige L¨osung istO(n)
7
Zusammenfassung
IVorteile:
I Vorg¨anger/NachfolgerO(1)
INachteile:
I Einf¨ugen/L¨oschenO(n)
8
Graphen als unendliche Datentypen
IEin Graph ist eine Liste von Knoten
data Node a b = Node a [ Vertex a b ] data Vertex a b = Vertex b ( Node a b ) type Graph a b = [ Node a b ]
IDamit Beispielgraph:
g : : Graph S t r i n g S t r i n g
g = l e t n1 = Node ”a” [ Vertex ”R” n2 ]
n2 = Node ”b” [ Vertex ”L” n1 , Vertex ”D” n3 ] n3 = Node ” c ” [ Vertex ”U” n1 ]
i n g
IProblem:Einf¨ugen/L¨oschen (King & Launchbury, 1995)
9
Graphen als induktive Datenstrukturen
IMartin Erwig (2001)— Functional Graph Library (FGL)
IIdeen:
1. Knoten habenexplizite Identit¨at 2. Graph istinduktivdefiniert IEin Graph ist
I entwederleer
I oderErweiterungeines Graphen (Kontext) type Node = I n t
type Adj b = [ ( b , Node ) ]
type Ctx a b = ( Adj b , Node , a , Adj b ) data Gr a b = Empty | Ctx a b :& Gr a b
10
Pattern Matching
IGraphkeinfreierDatentyp — Beispiel IDatentypGraphmussabstraktsein
IFallunterscheidung auf Konstruktoren vonGraphnicht m¨oglich IF¨alle:
ILeererGraph
isEmpty : : Gr a b→ Bool
INicht-leerer Graph:KontextplusRest
matchAny : : Gr a b→ ( Ctx a b , Gr a b )
11
Einfache Funktionen
IMatch auf einen bestimmten Knoten:
match : : Node→ Gr a b→ ( Maybe ( Ctx a b ) , Gr a b ) Invariante:
matchv g= (Just(is,w,l,os),h) =⇒v=w IMap:
gmap : : ( Ctx a b→ Ctx c d )→ Gr a b→ Gr c d gmap f g | isEmpty g = Empty
| o t h e r w i s e = f c :& (gmap f g ’ ) where ( c , g ’ ) = matchAny g
IDamit Umkehr aller Kanten:
swap : : Ctx a b→ Ctx a b swap ( p , v , l , s ) = ( s , v , l , p ) g re v : : Gr a b→ Gr a b
g re v = gmap swap
12
Beweise von Eigenschaften
IDatentypGr a bist nichtfrei, aberinduktiv IInduktion als zul¨assigesBeweisprinzip:
emptyg−→P g ∀c g.P g−→P(c: &g)
∀g.P g
IDamitzeigen:
gmapf.gmapf0 = gmap(f.f0) (gmap fusion)
grev.grev = id (grev inv)
13
Tiefensuche
IAufspannenden BauminTiefensuche data Tree a = Tree a [ Tree a ]
df : : [ Node ]→ Gr a b→ ( [ Tree Node ] , Gr a b ) df [] g = ( [] , g )
df ( v : vs ) g = c a s e match v g o f
( J u s t c , h ) → ( Tree v t1 : t2 , g2 ) where ( t1 , g1)= df ( suc c ) h
( t2 , g2)= df vs g1 ( Nothing , h ) → df vs h
d f f : : [ Node ]→ Gr a b→ [ Tree Node ] d f f vs g = f s t ( df vs g )
IAnwendung:SCC (stark verbundene Komponenten)
14
Breitensuche
IAufspannenden BauminBreitensuche
IProblem: Baum w¨achst nach ‘unten’ — daher Pfade type Path = [ Node ]
type RTree = [ Path ]
b f t : : Node→ Gr a b→ RTree b f t v = bf [ [ v ] ]
bf : : [ Path ]→ Gr a b→ RTree bf [] g = []
bf (p@( v : ) : ps ) g = c a s e match v g o f
( J u s t c , h ) → p : bf ( ps ++ map ( : p ) ( suc c ) ) h ( Nothing , h ) → bf ps h
IVerbesserung:Queue Pathstatt[Path]benutzen IAnwendung:k¨urzester Pfad
FGL als B¨ ucherei
IViele weitere Algorithmen
IGraphen alsKlasse, verschiedene Implementationen
IHackage:Data.Graph.Inductive
IMehr hier:
http://web.engr.oregonstate.edu/˜erwig/fgl/haskell/
Zusammenfassung
IUnendliche Datenstrukturenrealisiert durch Referenzen
IProgrammierung: Observatoren/Destruktoren vs. Konstruktoren
IBeispiel: doppelt verkettete Listen IGraphen in Haskell
IBeispiel f¨urinduktive, aber nichtfreieDatenstruktur
IKompakte Darstellung, effiziente Algorithmen m¨oglich
IN¨achste Woche: Nebenl¨aufigkeit
17
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 01.12.09:
Grundlagen der Nebenl¨aufigkeit in Haskell
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen ITeil II: Fortgeschrittene Datenstrukturen ITeil III: Nebenl¨aufigkeit
I Grundlagen
I Abstraktionen und Ausnahmebehandlung
I Software Transactional Memory ITeil IV: The Future of Programming
2
Heute gibt’s hier
Nebenl¨aufigkeit
IGrundkonzepte
IImplementation in Haskell
IBasiskonzepte
3
Konzepte der Nebenl¨ aufigkeit
I Thread (lightweight process) vs. Prozess Programmiersprache/Betriebssystem Betriebssystem (z.B. Java, Haskell, Linux)
gemeinsamerSpeicher getrennterSpeicher
Erzeugungbillig Erzeugungteuer
mehrereproProgramm einerproProgramm
IMultitasking:
I pr¨aemptiv:Kontextwechsel wirderzwungen
I kooperativ:Kontextwechsel nurfreiwillig
4
Zur Erinnerung: Threads in Java
IErweiterung der KlassenThreadoderRunnable IGestartet wird Methoderun()— durch eigene ¨uberladen IStarten des Threads durch Aufruf der Methodestart() IKontextwechsel mityield()
IJe nach JVM kooperativoderpr¨aemptiv.
ISynchronisation mitsynchronize
5
Threads in Haskell: Concurrent Haskell
ISequentiellesHaskell: Reduktion eines Ausdrucks
I Compiler legt Reihenfolge fes (outermost leftmost — verz¨ogerte Auswertung)
INebenl¨aufigesHaskell: Reduktion eines Ausdrucks anmehreren Stellen Ighcundhugsimplementieren Haskell-Threads
Ighc:pr¨aemptiv,hugs:kooperativ
IModulControl.Concurrententh¨alt Basisfunktionen IWenige Basisprimitive, darauf aufbauend Abstraktionen
6
Wesentliche Typen und Funktionen
IJeder Thread hat einen Identifier: abstrakter TypThreadId INeuen Thread erzeugen:forkIO:: IO()-¿ IO ThreadId IThread stoppen:killThread :: ThreadId -¿ IO () IKontextwechsel:yield :: IO ()
IEigener Thread:myThreadId :: IO ThreadId IWarten:threadDelay :: Int -¿ IO ()
Rahmenbedingungen
IZeitscheiben:
I Tick: Default 20ms
I Contextswitchpro Tick bei Heapallokation
I ¨Anderungen perKommandozeilenoptionen:+RTS -V¡time¿ -C¡time¿
IBlockierung:
I Systemaufrufe blockierenalle Threads
I Mit threaded library (-threaded) nicht alle
I Aber: Haskell Standard-IO blockiertnur den aufrufenden Thread
Concurrent Haskell — erste Schritte
IEin einfaches Beispiel:
w r i t e : : Char→ IO ( )
w r i t e c = putChar c w r i t e c main : : IO ( )
main = f o r k I O ( w r i t e ’X ’ ) w r i t e ’O’
IAusgabeghc: (X∗|O∗)∗ IAusgabehugs: (X∗|O∗)
9
Synchronisation mit MVars
IBasissynchronisationmechanismusin Concurrent Haskell
I Alles andereabgeleitet
IMVar aver¨anderbareVariable (vgl.IORef a) IEntwederleerodergef¨ulltmit Wert vom Typa IVerhalten beim Lesen und Schreiben
Zustand vorher: leer gef¨ullt
Lesen blockiert(bis gef¨ullt) danach leer Schreiben danach gef¨ullt blockiert(bis leer)
I NB.Aufweckenblockierter ProzesseeinzelninFIFO
10
Basisfunktionen MVars
INeue Variable erzeugen (leer oder gef¨ullt):
newEmptyMVar : : IO (MVar a ) newMVar : : a → IO (MVar a )
ILesen:
takeMVar : : MVar a → IO a
ISchreiben:
putMVar : : MVar a → a → IO ( )
11
Abgeleitete Funktionen MVars
INicht-blockierendes Lesen/Schreiben:
tryTakeMVar : : MVar a → IO ( Maybe a ) tryPutMVar : : MVar a→ a→ IO Bool
I ¨Anderung 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
12
Ein einfaches Beispiel ohne Synchronisation
INebenl¨aufige Eingabe von der Tastatur
import C o n t r o l . Monad( f o r e v e r , r e p l i c a t e M ) import C o n t r o l . Concurrent
echo : : S t r i n g→ IO ( ) echo p = f o r e v e r $ do
putStrLn ( ”∗∗∗ P l e a s e e n t e r l i n e f o r ”++p ) l i n e ← g e t L i n e
n←randomRIO (1 ,100)
r e p l i c a t e M n $ p u t S t r ( p++ ” : ”++ l i n e++” ” ) main : : IO ( )
main = f o r k I O ( echo ”2” ) echo ”1”
IProblem: gleichzeitige Eingabe IL¨osung:MVarsynchronisiert Eingabe
13
Ein einfaches Beispiel mit Synchronisation
IMVarvoll⇔Eingabe m¨oglich
I Also: initial voll
IInhalt der MVar irrelevant:MVar () echo : : MVar ( )→ S t r i n g→ IO ( ) echo f l a g p = f o r e v e r $ do
takeMVar f l a g
putStrLn ( ”∗∗∗ P l e a s e e n t e r l i n e ”++ p ) l i n e ← g e t L i n e
n←randomRIO (1 ,100)
r e p l i c a t e M n $ p u t S t r ( p++ ” : ”++ l i n e++” ” ) putMVar f l a g ( )
main : : IO ( )
main = do f l a g ←newMVar ( )
f o r k I O ( echo f l a g ”3” ) f o r k I O ( echo f l a g ”2” ) echo f l a g ”1”
14
Das Standardbeispiel
ISpeisende Philosopen
IPhilosophi:
Ivor dem Esseni-tes und(i+1)modn-tes St¨abchen nehmen
Inach dem Essen wieder zur¨ucklegen
ISt¨abchen modelliert alsMVar ()
15
Speisende Philosophen
p h i l o : : [ MVar ( ) ] → I n t→ IO ( ) p h i l o c h o p s t i c k s i = f o r e v e r $ do
l e t num phil = l e n g t h ( c h o p s t i c k s )
−−Thinking:
putStrLn ( ” P h i l #”++ show i ++” t h i n k s . . . ” ) randomRIO (10 , 200)=t h r e a d D e l a y
−−Get ready to eat:
takeMVar ( c h o p s t i c k s ! ! i )
takeMVar ( c h o p s t i c k s ! ! ( ( i +1) ‘mod ‘ num phil ) )
−−Eat:
putStrLn ( ” P h i l #”++ show i ++” e a t s . . . ” ) randomRIO (10 , 200)=t h r e a d D e l a y
−−Done eating:
putMVar ( c h o p s t i c k s ! ! i ) ( )
putMVar ( c h o p s t i c k s ! ! ( ( i +1) ‘mod ‘ num phil ) ) ( )
16
Speisende Philosophen
IHauptfunktion:nSt¨abchen erzeugen IAnzahl Philosophen in der Kommandozeile
main = do a : ← getArgs l e t num= read a
c h o p s t i c k s ← r e p l i c a t e M num $ newMVar ( ) mapM ( f o r k I O . ( p h i l o c h o p s t i c k s ) ) [ 0 . . num−1]
b l o c k
IHilfsfunktionblock: blockiert aufrufenden Thread b l o c k : : IO ( )
b l o c k = newEmptyMVar=takeMVar INB: Hauptthread terminiert — Programm terminiert!
17
Abstraktion: Semaphoren
IAbstrakter DatentypQSem
IBetretenkritischer Abschnitt(P):waitQSem :: QSem→IO () IVerlassenkritischer Abschnitt(V):signalQSem :: QSem→IO () ISemaphore: Z¨ahler plus evtl. wartende Threads
I Perniedrigt Z¨ahler, blockiert ggf. aufrufenden Thread
I Verh¨oht Z¨ahler, gibt ggf. blockierte Threads frei
IImplementierung von Semaphoren mitMVar: eigenes Scheduling IVariation:Quantitative Semaphoren
I Z¨ahler kann um Parameternerh¨oht/erniedrigt werden
18
Semaphoren: die P-Operation
data QSem = QSem (MVar ( Int , [ MVar ( ) ] ) ) IMVar ..f¨ur die ganze Semaphore, darin:
IZ¨ahler der Prozesse im kritischen Abschnitt
IListe von wartenden Prozessen (MVar ()) newQSem : : I n t → IO QSem
newQSem n = do m←newMVar ( n , [] ) r e t u r n (QSem m)
19
Semaphoren: die P-Operation
IEintrittin kritischen Abschnitt
IWenn Eintritt m¨oglich, Z¨ahler erniedrigen IAnsonsten blockieren (Reihenfolge!) waitQSem : : QSem → IO ( ) waitQSem (QSem sem ) = do
( a v a i l , b l o c k e d )← takeMVar sem i f a v a i l > 0 then
putMVar sem ( a v a i l−1, [] ) e l s e do
b l o c k ←newEmptyMVar
putMVar sem (0 , b l o c k e d++ [ b l o c k ] ) takeMVar b l o c k
20
Semaphoren: die V-Operation
IVerlassendes kritischen Abschnitts IFalls wartende threads, einen aufwecken.
IAlternatives Scheduling:
Iam Anfang hinzuf¨ugen, vom Anfang nehmen (einfacher,unfair)
Iam besten:zuf¨alligeAuswahl signalQSem : : QSem → IO ( ) signalQSem (QSem sem ) = do
( a v a i l , b l o c k e d ) ←takeMVar sem c a s e b l o c k e d o f
[] → putMVar sem ( a v a i l +1, [] ) b l o c k : blocked ’ → do
putMVar sem (0 , blocked ’ ) putMVar b l o c k ( )
21
Zusammenfassung
IConcurrent Haskellbietet
I Threadsauf Quellsprachenebene
I Synchronisierung mitMVars
I Durchschlankes Designeinfache Implementierung IFunktionales Paradigma erlaubtAbstraktionen
I Beispiel:Semaphoren
IN¨achste Woche:Kan¨aleundAusnahmen.
22
Fortgeschrittene Techniken der Funktionalen Programmierung Vorlesung vom 08.12.09:
Nebenl¨aufigkeit in Haskell: Abstraktionen und Ausnahmen
Christoph L¨uth, Dennis Walter Universit¨at Bremen Wintersemester 2009/10
1
Fahrplan
ITeil I: Monaden und fortgeschrittene Typen ITeil II: Fortgeschrittene Datenstrukturen ITeil III: Nebenl¨aufigkeit
I Grundlagen
I Abstraktionen und Ausnahmebehandlung
I Software Transactional Memory
ITeil IV: The Future of Programming
2
Tagesmen¨ u
IAbstraktionen:Kan¨ale IFallbeispiel:
ITalk
IAusnahmebehandlung:
IErweiterbare Ausnahmen
IUnscharfe Ausnahmen
IAsynchrone Ausnahmen
3
Kan¨ ale
ITypsicheres Lesen/Schreiben inFIFO-Ordnung
IBlockiertwenn leer data Chan a . . .
newChan : : IO ( Chan a )
writeChan : : Chan a → a → IO ( ) readChan : : Chan a → IO a
I Bonus:Duplizierbar (“Broadcast”)
4
Kan¨ ale
IEin Kanal besteht aus Strom mit einem Lese- und Schreibende:
data Chan a = Chan (MVar ( Stream a ) ) (MVar ( Stream a ) )
IHierMVar, um Lesen/Schreiben zu synchronisieren IEin Strom istMVar (ChItem a):
Ientweder leer,
Ioder enth¨alt Werte aus Kopfaund Rest.
type Stream a = MVar ( ChItem a ) data ChItem a = ChItem a ( Stream a )
5
In einen Kanal schreiben
INeues Ende (hole) anlegen IWert in altes Ende schreiben
IZeiger auf neues Ende setzen
writeChan : : Chan a → a → IO ( ) writeChan ( Chan w r i t e ) v a l = do
new hole ←newEmptyMVar o l d h o l e ←takeMVar w r i t e
putMVar o l d h o l e ( ChItem v a l new hole ) putMVar w r i t e new hole
IKann nicht blockieren —writeimmer gef¨ullt.
IOriginal-Code benutztmodifyMVar— Ausnahmesicher!
6
Aus Kanal lesen
IAnfang auslesen, Anfangszeiger weitersetzen IKann blockieren(*)wenn Kanal leer
readChan : : Chan a → IO a readChan ( Chan read ) = do
r e a d e n d ←takeMVar read
( ChItem v a l new read end ) ←readMVar r e a d e n d −−* putMVar read new read end
r e t u r n v a l
IreadMVar :: MVar a→IO aliestMVar, schreibt Wert zur¨uck.
IreadMVarstatttakeMVar, um Duplikation zu erm¨oglichen
7
Neuen Kanal erzeugen
ILese-Ende = Schreib-Ende newChan : : IO ( Chan a ) newChan = do
h o l e ←newEmptyMVar read ←newMVar h o l e w r i t e ←newMVar h o l e r e t u r n ( Chan read w r i t e )
8
Weitere Kanalfunktionen
IZeichen wieder vorne einh¨angen:
unGetChan : : Chan a → a → IO ( )
IKanal duplizieren (Broadcast):
dupChan : : Chan a → IO ( Chan a )
IKanalinhalt als (unendliche) Liste:
getChanContents : : Chan a → IO [ a ]
IAuswertung terminiert nicht, sondern blockiert
9
Fallbeispiel: Talk
IZiel: ein Programm, um sich ¨uber das Internetz zu unterhalten (talk, IRC, etc.)
IVerteilte Architektur:
Client Client Client
Client Server
IHier: Implementierung des Servers
I Netzverbindungen durchSocket
10
Socketprogrammierung
ISocket erzeugen, an Namen binden, mitlisten Verbindungsbereitschaft anzeigen
IZustandsbasierte Verbindung:
IServerseite: mitacceptauf eingehende Verbindungen warten
IJede Verbindung erzeugt neuen Filedescriptor
⇒inh¨arent nebenl¨aufiges Problem!
IClientseite: MitconnectVerbindung aufnehmen.
IZustandslose Verbindung:sendTozum Senden,recvFromzum Empfangen.
IGHC-ModulNetwork
ILow-level Funktionen inNetwork.Socket
11
Das Modul Network
ISockets:
type Socket
data PortID = S e r v i c e S t r i n g −−z.B. ”ftp”
| PortNumber PortNumber
| UnixSocket S t r i n g −−Socket mit Namen type Hostname = S t r i n g
i n s t a n c e Num PortNumber IZustandsbasiert:
l i s t e n O n : : PortID→ IO Socket
accept : : Socket→ IO ( Handle , Hostname , PortNumber ) connectTo : : Hostname → PortID → IO Handle
IZustandslos:
sendTo : : HostName → PortID → S t r i n g → IO ( ) recvFrom : : HostName → PortID → IO S t r i n g
12
Serverarchitektur
IEin Kanal zur Nachrichtenverbreitung:
Ieine Nachricht, viele Empf¨anger (broadcast)
IRealisierung mittelsdupChan IZentraler Scheduler
IF¨ur jede ankommende Verbindung neuer Thread:
INachrichten vom Socket auf den Kanal schreiben
INachrichten vom Kanal in den Socket schreiben
IProblem: Wie aus SocketoderKanal lesen wenn beide blockieren?
IL¨osung: Zwei Threads IClient:telnet
13
Talk 0.1: Hauptprogramm
main =do a : ← getArgs
l e t p = f r o m I n t e g e r ( read a ) s ← l i s t e n O n ( PortNumber p ) ch←newChan
loop s ch
14
Talk 0.1: Hauptschleife
loop s ch = f o r e v e r $ do ( handle , wh , p )← accept s h S e t B u f f e r i n g handle No B uffering
putStrLn $ ”New c o n n e c t i o n from ”++ wh++
” on p o r t ”++ show p
ch2 ←dupChan ch
f o r k I O ( newUser handle ch2 )
Talk 0.1: Benutzerprozess
newUser : : Handle→ Chan S t r i n g → IO ( ) newUser s o c k e t msgch =
f o r k I O ( f o r e v e r read ) f o r e v e r w r i t e where read : : IO ( )
read = hGetLine s o c k e t =writeChan msgch w r i t e : : IO ( )
w r i t e = readChan msgch=hPutStrLn s o c k e t