14 · Monadische Berechnungen Functor · 14.3 Partiell angewandte Typkonstruktoren Typfehler auf Typebene Natürlich kann man einen Typkonstruktor auch partiell anwenden: 1 2 3 4 5 6 � Prelude> :k Either Either :: * -> * -> * Prelude> :k Either Int Either Int :: * -> * Prelude> :k Either Int Bool Either Int Bool :: * 1 2 3 4 5 6 Prelude> (,) :: * Prelude> (,) (Int Prelude> (,) (Int :k -> :k -> :k -> (,) * -> * (,) (Int -> Bool) Bool) :: * -> * (,) (Int -> Bool) [Char] Bool) [Char] :: * Bei der Klassendefinition wird der Kind des Parameters festgelegt: 1 2 3 � Prelude> :i Functor — Ältere GHC-Versionen zeigen den Kind nicht an. class Functor (f :: * -> *) where fmap :: (a -> b) -> f a -> f b Bei der Instanziierung muss der Typ dann den gleichen Kind haben: 1 2 3 4 5 *Main> instance Functor Either where fmap = undefined <interactive>:7:18: Expecting one more argument to ‘Either’ The first argument of ‘Functor’ should have kind ‘* -> *’, but ‘Either’ has kind ‘* -> * -> *’ Stefan Klinger · DBIS Informatik 2 · Sommer 2016 476 14 · Monadische Berechnungen Functor · 14.3 Fazit Für Functor brauchen wir einen Typ vom Kind ∗ → ∗, also einen Typkonstruktor dem noch genau ein Typparameter fehlt. Beispiele 1 2 3 instance Functor (Either τ ) where fmap f (Left l) = Left l fmap f (Right x) = Right $ f x — Dem Either τ fehlt noch der Typ für Right. 4 5 6 — eigentlich: Functor (τ ,) where instance Functor ((,) τ ) where fmap f (x,y) = (x, f y) Frage Was ist der Typ von fmap bei diesen Instanziierungen? Erinnerung: class Functor f where fmap :: (α→ β) → f α → f β Stefan Klinger · DBIS Informatik 2 · Sommer 2016 477 14 · Monadische Berechnungen Functor · 14.3 Functor ohne Container Man kann sich vorstellen dass fmap eine Funktion innerhalb eines Kontexts f anwendet, also in den Kontext hebt (engl. lifting), ohne den Kontext zu verändern. � 1 2 Beispiele Der Kontext muss dabei kein Container sein. instance Functor ((→) τ ) where fmap = ... — wie geht das? — eigentlich: instance Functor (τ →) where Dabei ist (τ →) der Typkonstruktor, welcher Funktionen von τ konstruiert. Frage 1 Was ist der Typ von fmap bei dieser Instanziierung? Erinnerung: class Functor f where fmap :: (α→ β) → f α → f β Frage 2 Was also ist fmap? Stefan Klinger · DBIS Informatik 2 · Sommer 2016 478 14 · Monadische Berechnungen Functor · 14.3 Antwort 1 fmap :: (α → β) → (τ → α) → (τ → β) Das erhält man durch einfaches Einsetzen von (τ →) für f in die Deklaration von fmap. Antwort 2 Dieser Typ sollte bekannt vorkommen: Die Komposition. � Setzen fmap = (◦) 1 2 � instance Functor ((→) τ ) where fmap = (.) Prüfen die Funktor-Eigenschaft: Für alle Funktionen g gilt fmap id g � id ◦ g � λx. id (g x) � λx. g x � g ≡ id g β β β η Anwendung 1 2 3 4 5 6 *Main> :t length length :: [a] → Int *Main> :t (<3) (<3) :: Int -> Bool *Main> :t fmap (<3) length fmap (<3) length :: [a] → Bool Stefan Klinger · DBIS � � Aus [α] → Int wird [α] → Bool. In einer Funktion auf [α] haben wir den Ergebnistyp von Int in Bool verwandelt. Informatik 2 · Sommer 2016 479 14 · Monadische Berechnungen Functor · 14.3 Mehr Argumente � 1 2 3 4 5 Bisher hatten wir Funktionen mit einem Argument über einen Functor gemappt: *Main> :t even — hier mit einfacherem Typ even :: Int → Bool � Wichtig war der Inhalt der Struktur (Int), der als Argument zur Funktion even passen muss. � Die Struktur selbst war nicht weiter wichtig: Maybe, [·], ([α] →), ... *Main> :t fmap even $ Just 42 fmap even $ Just 42 :: Maybe Bool 6 7 8 *Main> :t fmap even [1..] fmap even [1..] :: [Bool] 9 10 11 *Main> :t fmap even length fmap even length :: [α] → Bool Frage Welche Typen sind bei Funktionen mit mehreren Argumenten zu erwarten? Beispiel: (unter der Annahme, dass (<) :: Int → Int → Bool) fmap (<) [1, 2, 3] Stefan Klinger · DBIS Informatik 2 · Sommer 2016 480 14 · Monadische Berechnungen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Functor · 14.3 *Main> :t (<) (<) :: Int → Int → Bool *Main> :t fmap (<) fmap (<) :: Functor f ⇒ f Int → f (Int → Bool) *Main> :t fmap (<) $ Just 42 fmap (<) $ Just 42 :: Maybe (Int → Bool) *Main> :t fmap (<) [1..] fmap (<) [1..] :: [Int → Bool] *Main> :t fmap (<) length fmap (<) length :: [α] → Int → Bool � Die Strukturen enthalten Funktionen. Also gerade das, was (+) angewendet auf einen Int liefert. � Das ist nicht weiter überraschend, aber ekelhaft, denn. . . Offensichtliche Frage Wie wenden wir etwas vom Typ f (α → β) auf etwas vom Typ f α an? Wie können wir so eine Funktion verwenden? Stefan Klinger · DBIS Informatik 2 · Sommer 2016 481