Funktionale Programmierung 10.5.2005 1 Das Funktionale Quiz In C sind Pointer auf Funktionen Werte. Sind Funktionen also in C higher-oder? Nein, es gibt nur Funktionsdefinitionen auf oberster Ebene. 2-3 Das Funktionale Quiz Woran ist das x in add3 im Rumpf von g gebunden und warum? add x = (\y -> x + y) add3 = add 3 g x = add3 4 3, wegen statischer Bindung 4-5 Das Funktionale Quiz Was weiß man über den Rumpf von f? data Foo = Bar Int f :: Int -> Foo Ergebnis wird mit Bar berechnet 6-7 Themen heute • Deforestation • Kombination von Typklassen • Konstruktorklassen • Das Modulsystem von Haskell 8 Theoreme helfen dem Compiler: Deforestation Beob: Oft werden Listen erzeugt und sofort wieder konsumiert Idee: Wende Konsument anstelle der Listenkonstruktoren an toZero :: Int -> [Int] toZero 0 = [] toZero n = n:(toZero (n - 1) sum [] =0 sum (x:xs) = x + (sum xs) 9 Kombination sumToZero :: Int -> Int sumToZero = sum . toZero Ohne Zwischenliste: sumToZero 0 = 0 sumToZero n = n + sumToZero (n - 1) 10 Deforestation f :: a -> [b] f pat1 = [] f pat2 = e1:(f e2) g :: [b] -> c g [] = eNil g (x:xs) = ... x ... (g xs) gDotf :: a -> c gDotf pat1 = eNil gDotf pat2 = ... e1 ... (g (f e2)) 11 Mit Abstraktion • Abstraktion über primitive Rekursion: foldr • Abstraktion über Erzeugen der Listen: build g = g (:) [] mit g :: (a -> b -> b) -> b -> b • Beispiel: replicate i x = build (repl i x) where repl 0 x c n = n repl i x c n = c x (repl (i - 1) x c n) 12 Deforestation mit foldr/build • Es gilt: foldr f c (build g) = g f c • Also Deforestation: Zwischenliste wird nicht erzeugt • build zu verwenden ist umständlich, aber Eingebaute Funktionen damit definieren List-Comprehension dahin Übersetzen Compiler versucht, nach build umzuformen 13 Typklassen kombinieren Typklasse für bewegliche 2D-Objekte: data Vector = Vec Float Float class Movable a where move :: Vector -> a -> a 14 2D-Punkte und Figuren data Point = Point Float Float deriving Show instance Movable Point where move (Vec v1 v2) (Point x y) = Point (x+v1) (y+v2) data Figure = Line Point Point | Circle Point Float deriving Show instance Movable Figure where move v (Line p1 p2) = Line (move v p1) (move v p2) move v (Circle p r) = Circle (move v p) r 15 Typklasse für Objekte mit Namen class Named a where lookName :: a -> String giveName :: String -> a -> a 16 Container für Objekte mit Namen data Name a = Pair a String deriving Show instance Named (Name a) where lookName (Pair obj nm) = nm giveName new (Pair obj nm) = (Pair obj new) 17 Namen für Movables • Erweitern ("Liften") beliebiger Operationen auf Objekte inName: mapName :: (a -> b) -> Name a -> Name b mapName f (Pair obj nm) = Pair (f obj) nm • Damit Objekte mit Namen zu Movable hinzufügen: instance Movable a => Movable (Name a) where move v = mapName (move v) • Jetzt funktionieren move und ...Name: a = Pair (Point 0 0) "Punkt A" move a --> Pair (Point 1.0 2.0) "Punkt A" lookName a --> "Punkt A" 18 Kombination der Typklassen • Typklasse für 2D-Objekte mit Namen class (Movable b, Named b) => NamedMovable b • Movable eingewickelt in Name sind Mitglieder von NamedMovable: instance Movable a => NamedMovable (Name a) 19 map auf verschiedenen Typen mapList :: (a -> b) -> [a] -> [b] mapList f [] = [] mapList f (x:xs) = f x : map f xs mapTree :: (a -> b) -> Tree a -> Tree b mapTree f Leaf = Leaf mapTree f (Node a t1 t2) = Node (f a) (mapTree t1) (mapTree t2) mapMaybe :: (a -> b) -> Maybe a -> Maybe b mapMaybe f Nothing = Nothing mapMaybe f (Just a) = Just (f a) 20 map überladen mapList :: (a -> b) -> [a] -> [b] mapTree :: (a -> b) -> Tree a -> Tree b mapMaybe :: (a -> b) -> Maybe a -> Maybe b Abstraktion über Typkonstruktor map :: (a -> b) -> c a -> c b c ist [], Tree oder Opt 21 Konstruktorklasse Klasse von Typkonstuktoren class Functor f where map :: (a -> b) -> f a -> f b Instanzen: instance Functor [] where map f [] = [] map f (x:xs) = f x : map f xs instance Functor Tree where map = mapTree instance Functor Maybe where map = mapMaybe 22 Kind: Stelligkeit von Typkonstruktoren In map :: (a -> b) -> f a -> f b ist f ein einstelliger Typkonstuktor Int oder Bool können nicht Instanzen von Funktor sein, sie akzeptieren keine Parameter (,) auch nicht, er akzeptiert 2 Parameter Stelligkeit wird repräsentiert durch "Kind": • Int, Bool :: * • [],Tree,Maybe :: * -> * • (,) (->) :: * -> * -> * Faustregel: Anzahl Typvariablen = Anzahl Pfeile Für Funktor also * -> * 23 Beispiel: Konstruktorklasse für Folgen class Seq s where empty :: s a cons :: a -> s a -> s a isEmpty :: s a -> Bool hd :: s a -> a tl :: s a -> s a foldRight :: (a -> b -> b) -> b -> s a -> b foldRight c n s | isEmpty s = n | otherwise = c (hd s) (foldRight c n (tl s)) 24 Listen als Folgen instance Seq [] where empty = [] cons = (:) isEmpty [] = True isEmpty _ = False hd = head tl = tail foldRight = foldr 25 Bäume als Folgen (Preorder) instance Seq Tree where empty = Leaf cons a Leaf = Node a Leaf Leaf cons a (Node b t1 t2) = Node b (cons a t1) t2 isEmpty Leaf = True isEmpty _ = False hd (Node b Leaf _) = b hd (Node _ t1 _) = hd t1 tl (Node b Leaf t2) = t2 tl (Node b t1 t2) = Node b (tl t1) t2 26 Funktionen auf Folgen seqMap :: Seq s => (a -> b) -> s a -> s b seqMap g = foldRight (\el rest -> cons (g el) rest) empty seqLength :: Seq s => s a -> Int seqLength = foldRight (\ _ rest -> rest + 1) 0 seqToList :: Seq s => s a -> [a] seqToList = foldRight (:) [] 27 Mehr Operationen Besser: seqMap, seqLength, seqElem auch als Operationen der Konstruktorklasse definieren Vorteil: Instanzen könnten sie überschreiben class Seq s where ... seqToList :: s a - a seqToList = foldRight (:) [] instance Seq [] where ... seqToList l = l 28 Zusammenfassung Zusammenfassung Seq: • Sinnvolle Ergänzung: Abstraktion über generelle Rekursion • Dann wären mehr Defaultdeklarationen möglich Zusammenfassung Konstruktorklassen: • Erweiterung von Typklassen • Ermöglicht Abstraktion über Typrepräsentation • Später noch wichtige Konstruktorklasse Monad 29 Das Haskell-Modulsystem Ein Modul ist eine austauschbare Codeeinheit, die eine komplexe Leistung erbringt und eine eindeutige Rolle im System hat. Komponenten: • Definitionen • Schnittstelle • Importe 30 Moduldefinition • Nur auf oberster Ebene Syntax: module → module modid [exports] where body | body body → { impdecls ; topdecls } • module modid definiert Modul mit Namen modid • Sollte in Datei mit Namen modid .hs stehen • Fehlt Angabe der Exporte, werden alle Definitionen exportiert 31 Importe impdecl → import [qualified] modid [as modid] [impspec] impspec → ( import1, ... ,importn) | hiding ( import1, ... ,importn) • Importiert Bezeichner aus Modul modid • Suchpfad ist Installation und momentanes Verzeichnis (Vorsicht im XEmacs!) • Mit qualified nur als Module.name ("qualifiziert") • as vergibt neuen Modulnamen für qualifizierte Namen • Ist Importspezifikation vorhanden, so legt sie fest, welche der exportierten Namen importiert werden • Andernfalls alle 32 Einträge der Import-Spezifikationen import → var | tycon [ (..)| ( cname1, ... , cnamen)] | tycls [ (..)| ( var1 , ... , varn)] cname → var | con • Namen aus Definitionen • Typdefinitionen mit (..) allen Konstruktoren (cname1, ... , cnamen) ausgewählten Konstruktoren • Analog für Typklassen 33 Exporte exports → ( export1, ... , exportn) export → qvar | qtycon [(..)| ( cname1, ... , cnamen)] | qtycls [(..)| ( var1 , ... , varn)] | module modid • Analog zu Importen • module modid re-exportiert Exporte eines Imports 34 Das Modul Prelude • Wird immer implizit importiert • Außer, wenn es explizit importiert wird • import Prelude (...) importiert also selektiv aus der Prelude 35 Beispiele module Foo (a) import List a = nub [1,2,1,2] b = 23 • Exportiert a • Importiert List 36 Beispiele module Bar (Stack (..),stackIsEmpty) import Prelude hiding (head) data Stack a = Stk [a] stackIsEmpty (Stk []) = True stackIsEmpty _ = False head (Stack (x:xs)) = x • Importiert alles aus der Prelude außer head • Exportiert alg. Datentyp mit Konstruktor Stk 37 Abstrakte Datentypen in Haskell • ATD = Datentyp bei dem die Repräsentation nicht nach außen gegeben wird • Sinn: Implementierung kann ausgetauscht werden • In Haskell: Modul mit algebraischem Datentyp dessen Konstruktoren nicht exportiert werden 38 Beispiel-ADT: Speicher module Store (Store, initial, value, update) where import qualified List data Store = Sto [(Int, String)] initial = Sto [] value (Sto l) addr = x where Just x = lookup l addr update (Sto l) addr val = Sto (addr,val):l • Außerhalb kein Matching möglich, da Sto nicht exportiert 39 newtype Algebraischer Datentyp mit nur einen Konstruktor = neuer Typ mit bekannter Repräsentation Bsp. siehe Store Effizienter mit newtype Syntax: Wie data aber nur ein Konstruktor 40