-- Übungsblatt 1
-- =============
--
-- Typtheorie
-- ----------
--
-- Schreiben Sie alle **möglichen** Implementationen der folgenden
-- Funktionen. Wozu könnte `fun2` nützlich sein?

fun1 :: a -> a
fun1 a = a

-- id

fun2 :: a -> b -> a
fun2 a _ = a

fun3 :: (Eq a) => a -> a -> Bool
fun3 _ _ = True
--fun3 _ _ = False
--fun3 = (==)
--fun3 = (/=)

-- Wir haben in der Vorlesung parametrisierte Typen kennengelernt. Der
-- simpelste hiervon ist `Identity`, der nur einen anderen Typen einpackt.

data Identity a = Identity a

-- Diese Definition stellt uns automatisch den Konstruktor
-- `Identity :: a -> Identity a` zur Verfügung, der ein `a` einpackt.
-- Schreiben Sie die Funktion

unIdentity :: Identity a -> a
unIdentity (Identity a) = a

-- welche diesen Vorgang wieder rückgängig macht.
--
-- Angenommen, Sie hätten nun ein Wert vom Typen `Identity a` und eine
-- Funktion mit dem Typen `a -> b`. Wie wenden Sie diese auf das `a`
-- "innerhalb" des `Identity` an um ein `Identity b` herzustellen?
-- Schreiben Sie also eine Funktion

mapIdentity :: (a -> b) -> Identity a -> Identity b
mapIdentity f = Identity . f . unIdentity

--mapIdentity f (Identity a) = Identity (f a)

-- **Hinweis:** Es gibt *zwei* prinzipielle Vorgehen dieses zu
-- implementieren. Kommen Sie auf beide?
--
-- Funktionen sind auch nur Typen
-- ------------------------------
--
-- Datentypen können auch Funktionen enthalten. Sehen Sie sich einmal den
-- Datentypen

data Pred a = Pred (a -> Bool)

-- an. Hier wird ein Prädikat definiert, welches (gegeben einen Datentyp
-- `a`) eine Funktion gespeichert hat, die `a` in einen `Bool` umwandeln
-- kann (etwa um irgendwas zu filtern/selektieren/löschen/..., wenn man
-- dies an eine weitere Funktion übergibt).
--
-- Auch hier können Sie eine Funktion schreiben, die das `Pred a` wieder
-- "auspackt". Definieren Sie

unPred :: Pred a -> a -> Bool
unPred (Pred a) = a

-- Da Haskell-Funktionen aber "gecurried" sind (mehr dazu in der
-- Vorlesung), können Sie die Klammern hinten in der Signatur auch
-- weglassen und erhalten `unPred :: Pred a -> a -> Bool`, was man zugleich
-- als "wende `Pred a` an, wenn du ein `a` bekommst" lesen kann. In der Tat
-- sind beide Funktionen identisch (wieso?).
--
-- Bonus
--
-- Was für eine Funktion bräuchten Sie um ein `Pred a` in ein `Pred b`
-- umzuwandeln? Können Sie diese implementieren?

mapPred :: (b -> a) -> Pred a -> Pred b
mapPred f (Pred a) = Pred (a . f)

-- Neue Typen erfinden
-- -------------------
--
-- In Haskell ist ein zentraler Vorgehenspunkt das Definieren und Verwenden
-- von eigenen Datentypen. Zur Erinnerung; es gibt zwei Möglichkeiten, die
-- man miteinander kombinieren kann: `data Prod a b c = Prod a b c`
-- (Produkttyp) benötigt sowohl `a`, `b` als auch `c` um einen Wert zu
-- erzeugen, `data Sum a b = Sum1 a | Sum2 b` (Summentyp) braucht entweder
-- ein `a` um durch den Konstruktor `Sum1` ein `Sum a b` zu erzeugen oder
-- ein `b` um durch den Konstruktor `Sum2` ein `Sum a b` zu erzeugen.
--
-- Definieren Sie einen Datentypen `Vielleicht a`, der zwei Konstruktoren
-- besitzt: Einen Konstruktor, mit dem durch ein `a` ein `Vielleicht a`
-- konstruiert wird und ein zweiter Konstruktor, der keinen Wert nimmt,
-- sondern die "Abwesenheit eines `a`" symbolisieren soll.

data Vielleicht a = Etwas a
                  | Nichts

-- Können Sie hier eine Funktion schreiben, die das `a` extrahiert? Wenn
-- ja, implementieren Sie diese; wenn nein, geben Sie eine kurze
-- Begründung.
--
-- Wie würden Sie mittels einer Funktion `a -> b` ein `Vielleicht a` in ein
-- `Vielleicht b` wandeln? Implementieren Sie

mapVielleicht :: (a -> b) -> Vielleicht a -> Vielleicht b
mapVielleicht f (Etwas a) = Etwas (f a)
mapVielleicht f Nichts    = Nichts

-- Bonus
--
-- Man kann Typen natürlich auch Schachteln. Worin liegt eigentlich der
-- Unterschied zwischen einem `Pred (Vielleicht a)` und einem
-- `Vielleicht (Pred a)`? Oder sind diese identisch?

fun4 :: Pred (Vielleicht a) -> x
fun4 (Pred a) = undefined

fun5 :: Vielleicht (Pred a) -> x
fun5 (Etwas (Pred a)) = undefined
fun5 Nichts = undefined


main = putStrLn "compiles"
