Wir werden TypeFamilies
für diese Lösung benötigen.
{-# LANGUAGE TypeFamilies #-}
Die Idee ist, eine Klasse Pred
für n-stellige Prädikate zu definieren:
class Pred a where
type Arg a k :: *
split :: a -> (Bool -> r) -> Arg a r
Das Problem dreht sich alles um wieder schlurfenden Argumente zu den Prädikaten, so ist dies, was die Klasse zielt darauf ab, zu tun . Der zugehörige Typ Arg
soll Zugriff auf die Argumente eines n-stelliges Prädikat geben, indem die endgültige Bool
mit k
zu ersetzen, so dass, wenn wir eine Art
X = arg1 -> arg2 -> ... -> argn -> Bool
dann haben
Arg X k = arg1 -> arg2 -> ... -> argn -> k
Dies ermöglicht Wir erstellen den richtigen Ergebnistyp von conjunction
, in dem alle Argumente der beiden Prädikate gesammelt werden sollen.
Die Funktion split
nimmt ein Prädikat des Typs a
und eine Fortsetzung des Typs Bool -> r
und wird etwas vom Typ Arg a r
produzieren. Die Idee von split
ist, dass, wenn wir wissen, was mit der Bool
zu tun ist, wir aus dem Prädikat am Ende erhalten, dann können wir andere Dinge (r
) dazwischen tun.
Es überrascht nicht, wir werden zwei Instanzen benötigen, eine für Bool
und eine für Funktionen, für die das Ziel bereits ein Prädikat:
instance Pred Bool where
type Arg Bool k = k
split b k = k b
A Bool
keine Argumente hat, so Arg Bool k
einfach k
zurückgibt.Auch für split
haben wir die Bool
bereits, so dass wir sofort die Fortsetzung anwenden können.
instance Pred r => Pred (a -> r) where
type Arg (a -> r) k = a -> Arg r k
split f k x = split (f x) k
Wenn wir ein Prädikat vom Typ a -> r
haben, dann Arg (a -> r) k
müssen mit a ->
beginnen, und wir werden weiterhin durch Arg
rekursiv auf r
aufrufen. Für split
können wir nun drei Argumente annehmen, wobei x
vom Typ a
ist. Wir können x
zu f
führen und dann split
auf das Ergebnis aufrufen.
Sobald wir die Pred
Klasse definiert haben, ist es leicht, conjunction
zu definieren:
conjunction :: (Pred a, Pred b) => a -> b -> Arg a (Arg b Bool)
conjunction x y = split x (\ xb -> split y (\ yb -> xb && yb))
Die Funktion nimmt zwei Prädikate und gibt etwas vom Typ Arg a (Arg b Bool)
. Schauen wir uns das Beispiel an:
GHCi erweitert diesen Typ nicht, aber wir können. Der Typ entspricht
Ord a => a -> a -> Bool -> Bool
was genau wir wollen. Wir können eine Reihe von Beispielen testen, zu:
> conjunction (>) not 4 2 False
True
> conjunction (>) not 4 2 True
False
> conjunction (>) not 2 2 False
False
Beachten Sie, dass die Pred
Klasse verwenden, ist es trivial ist, andere Funktionen zu schreiben (wie disjunction
) auch.