-
Yannick Gottschalk authored
This reverts commit aab564e7.
Yannick Gottschalk authoredThis reverts commit aab564e7.
Aufgabe3.hs 5.28 KiB
-- Aufgabe 3
-- =========
module Aufgabe3 where
import FunPB
import DataPB
import AreaCode
import Data.Char
-- Functor – ein Container?
-- --------------------------
-- Zur Erinnerung:
-- class Functor f where
-- fmap :: (a -> b) -> f a -> f b
-- Aus der Vorlesung 2 kennen Sie bereits die `Functor`-Instanzen für `[]`, `Identity`
-- und einen binären Baum. Außerdem haben Sie gelernt, dass Sie auch für Ihre eigenen
-- Typen `Functor`-Instanzen definieren können. Eine Intuition dafür, ob sich für einen
-- Typ eine `Functor`-Instanz schreiben lässt, erhalten Sie, indem Sie sich fragen, ob
-- der Typ "eine Art Container" ist, auf deren Inhalte sich Funktionen (a -> b) anwenden
-- lassen. Was bedeutet dies in Haskell-Syntax? Hierfür ist es dienlich, sich den
-- `type constructor` des betreffenden Datentyps anzuschauen. Handelt es sich um einen
-- polymorphen Datentyp stehen die Chancen gut, dass es ein `Functor` ist.
-- data Bool = False | True
-- data Maybe a = Nothing | Just a
-- data (,) a b = (a,b)
-- `Bool` ist offenbar kein `Functor`, denn der Typkonstruktor `Bool` hat keine Parameter –
-- hier ist nur Platz für True und False. `Maybe a` dagegen hat einen Parameter, einen freien
-- Slot für alles mögliche. Der Tupeltyp `(,) a b` hat sogar zwei Parameter – er kann Dinge von
-- zwei verschiedenen Typen enthalten. Hier stellt sich die Frage, für welchen Container die
-- Functorinstanz definiert ist. Ähnlich wie Funktionen, lassen sich auch Typkonstruktoren
-- partiell anwenden. Für die Instanziierung werden dem Typkonstruktor daher alle bis auf ein
-- Parameter übergeben. Dieser letzte, freie Parameter legt dann den Inhalt des "Container"
-- fest. Beispiel:
-- instance Functor ((,) a) where --Die Functor-Instanz ist für (,) a definiert
-- fmap f (x,y) = (x, f y) --Also wird über Tupelslot 2 "gemapt"
-- Implementieren Sie `Functor`-Instanzen für die folgenden Datentypen:
data Vielleicht a = Nichts | Etwas a
deriving (Show,Eq)
instance Functor Vielleicht where
fmap = undefined
data Entweder a b = Jenes a | Dieses b
deriving (Show,Eq)
instance Functor (Entweder a) where
fmap = undefined
data Konstant a b = Konstant a
deriving (Show,Eq)
instance Functor (Konstant a) where
fmap = undefined
-- Achtung: Die "Container"-Metapher hat ihre Grenzen. Betrachten Sie hierzu noch einmal den
-- Datentyp Pred a von Zettel 1: `data Pred a = Pred (\a -> Bool)`.
data Pred a = Pred (a -> Bool)
runPred :: Pred a -> (a -> Bool)
runPred (Pred p) = p
-- `Pred a` macht den Anschein als handele es sich auch hier um einen Container mit Inhalt a.
-- Trotzdem lässt sich `Functor` hierfür nicht instanziieren. Der Unterschied liegt darin,
-- dass der Typaramter `a` als Input und nicht als Ergebnis in der vom Konstruktor `Pred`
-- "eingepackten" Berechnung auftaucht. Allerdings lässt sich hier auf eine sehr ähnliche
-- Eigenschaft abstrahieren, die wir für's Erste `InputFunctor` nennen wollen.
class InputFunctor f where
inputmap :: (a -> b) -> f b -> f a
-- Schreiben Sie eine `InputFunctor`-Instanz für `Pred a`.
instance InputFunctor Pred where
inputmap = undefined
-- Hiermit lässt sich nun bequem die folgende Funktion definieren, welche aus einem
-- `Pred Int` ein `Pred String` macht, das prüft, ob ein Eingabestring wenigstens die Länge 5 hat.
atLeast5 :: Pred Int
atLeast5 = Pred $ (\x -> x>=5)
atLeast5Char :: Pred String
atLeast5Char = inputmap length atLeast5
-- Functorial phone book
-- ---------------------
-- Jetzt noch einmal zurück zu PhoneBook aus Aufgabe 1.
type Number = String
type Name = String
type Entry = [Number]
newtype PhoneBook = PB (Name -> Entry)
-- `PhoneBook` hat keinen Parameter, aber die allgemeinere Version `FunPB` hat sogar zwei:
-- data FunPB a b = FunPB (a -> (a,[b]))
-- Beachten Sie, dass sich auch der Rückgabetyp ein wenig unterscheidet. Die Idee ist,
-- dass FunPB zusätzlich zu den assoziierten `Number`s (angenommen `b` ist `Number`) auch
-- den gesuchten `Name` (angenommen `a` ist `Name`) zurückgibt.
-- Implementieren Sie eine `Functor`-Instanz für `FunPB`.
-- Hinweis: Sie können benutzen, dass für `[]`, `((,) a)` und sogar für den "function arrow"
-- `((->) a)` bereits `Functor`-Instanzen in der [`GHC.Base`](https://hackage.haskell.org/package/base "GHC.Base") existieren.
instance Functor (FunPB a) where
fmap = undefined
-- Die Functor-Instanz erlaubt uns nun die Funktionen
-- `separateAreaCode :: Number -> (AreaCode,Number)` und `snd` zu verwenden, um ein
-- TelefonBuch mit separiertem bzw. ganz ohne AreaCode zu erhalten.
areaCodeFunPB :: FunPB Name String -> FunPB Name (AreaCode,Number)
areaCodeFunPB = fmap separateAreaCode
withoutAreaCodeFunPB :: FunPB Name Number -> FunPB Name Number
withoutAreaCodeFunPB = fmap (snd.separateAreaCode)
result = "Suche \"Paula\" in (FunPB Name Number): \n"
++ (show $ runFunPB (dataToFunPB simpleData) "Paula") ++ "\n"
++ "Suche \"Paula\" in (FunPB Name (AreaCode,Number)): \n"
++ (show $ runFunPB (areaCodeFunPB $ dataToFunPB simpleData) "Paula") ++ "\n"
++ "Suche \"Paula\" in (FunPB Name Number) ohne Vorwahl: \n"
++ (show $ runFunPB (withoutAreaCodeFunPB $ dataToFunPB simpleData) "Paula")