Partiell angewandte Typkonstruktoren

Werbung
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
Herunterladen