Skip to content
Snippets Groups Projects
Aufgabe3.hs 5.28 KiB
Newer Older
-- 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")