16 Stimmen

Polymorphismus innerhalb von Funktionen höherer Ordnung?

Ich habe einen algebraischen Datentyp mit einigen Konstruktoren, die vergleichbare Werte enthalten, und einigen Konstruktoren, die dies nicht tun. Ich habe einige Vergleichsfunktionen geschrieben, die wie die Standard (==) y (/=) Operatoren, sondern geben Nothing für Vergleiche, die keinen Sinn ergeben:

data Variant = IntValue Int
             | FloatValue Float
             | NoValue

equal :: Variant -> Variant -> Maybe Bool
equal (IntValue a) (IntValue b) = Just (a == b)
equal (FloatValue a) (FloatValue b) = Just (a == b)
equal _ _ = Nothing

unequal :: Variant -> Variant -> Maybe Bool
unequal (IntValue a) (IntValue b) = Just (a /= b)
unequal (FloatValue a) (FloatValue b) = Just (a /= b)
unequal _ _ = Nothing

Das funktioniert, aber die Wiederholungen sind unhandlich - zumal ich eigentlich mehr Variant Konstruktoren und weitere Vergleichsfunktionen.

Ich dachte, ich könnte Faktor aus der Wiederholung in eine Hilfsfunktion, die auf die Vergleichsfunktion parametrisiert ist:

helper :: (Eq a) => (a -> a -> Bool) -> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing

equal' :: Variant -> Variant -> Maybe Bool
equal' = helper (==)

unequal' :: Variant -> Variant -> Maybe Bool
unequal' = helper (/=)

aber das funktioniert nicht, weil die Typvariable a kann sich offenbar nicht an beide Int y Float gleichzeitig in der Definition von helper ; GHC bindet es an Float und beschwert sich dann über eine Typfehlanpassung in der Zeile, die die IntValue .

Eine Funktion wie (==) ist polymorph, wenn sie direkt verwendet wird; gibt es eine Möglichkeit, sie an eine andere Funktion zu übergeben und sie polymorph zu halten?

15voto

C. A. McCann Punkte 76279

Ja, das ist möglich, aber nur mit Spracherweiterungen :

{-# LANGUAGE Rank2Types #-}

helper :: (forall a. (Eq a) => (a -> a -> Bool))
       -> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing

El forall a. macht ungefähr das, wonach es klingt; die a wird innerhalb der Klammern universell quantifiziert und außerhalb der Klammern nicht berücksichtigt. Dies bedeutet, dass die f Argument muss polymorph über alle Typen a sein, die Instanzen von Eq und das ist genau das, was Sie wollen.

Die Erweiterung wird hier "Rang 2" genannt, weil sie den regulären Stil der Polymorphie im äußersten Bereich sowie polymorphe Argumente wie im Beispiel hier erlaubt. Um Dinge weiter zu verschachteln, benötigen Sie die Erweiterung RankNTypes was ziemlich selbsterklärend ist.

Nebenbei bemerkt, in Bezug auf polymorphe Typen höheren Ranges - denken Sie daran, dass die forall ist das, was die Variable tatsächlich an einen Typ bindet; man kann sich vorstellen, dass sie sich ähnlich wie ein Lambda verhält. Wenn Sie eine solche Funktion auf etwas mit einem konkreten Typ anwenden, wird der Typ des Arguments implizit durch die forall für diese Verwendung. Dies tritt zum Beispiel auf, wenn Sie versuchen, einen Wert zu verwenden, dessen Typ durch eine innere forall außerhalb dieser Funktion; der Typ des Wertes hat den Anwendungsbereich verlassen, was es schwierig macht, etwas Sinnvolles zu tun (wie Sie sich wahrscheinlich vorstellen können).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X