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