„Pattern Matching“ algebraischer Typdaten Konstruktoren
-
26-09-2019 - |
Frage
betrachten wir einen Datentyp mit vielen Konstrukteuren:
data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int
Ich mag eine Funktion schreiben, zu überprüfen, ob zwei Werte mit dem gleichen Konstruktor erzeugt werden:
sameK (Alpha _) (Alpha _) = True
sameK (Beta _) (Beta _) = True
sameK (Gamma _ _) (Gamma _ _) = True
sameK _ _ = False
sameK
Pflege ist nicht viel Spaß, es kann nicht für Richtigkeit leicht überprüft werden. Wenn zum Beispiel neue Konstruktoren T
hinzugefügt werden, ist es einfach zu aktualisieren sameK
zu vergessen. Ich weggelassen eine Zeile ein Beispiel zu geben:
-- it’s easy to forget:
-- sameK (Delta _) (Delta _) = True
Die Frage ist, wie Text in sameK
zu vermeiden? Oder, wie man sicherstellen, dass es Kontrollen für alle T
Bauer?
Die Abhilfe, die ich gefunden ist für jeden des Konstrukteurs separate Datentypen zu verwenden, Data.Typeable
abzuleiten, und eine gemeinsame Typklasse erklärt, aber ich weiß nicht, wie diese Lösung, weil es viel weniger lesbar und sonst nur ist eine einfache algebraische Art funktioniert für mich:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
class Tlike t where
value :: t -> t
value = id
data Alpha = Alpha Int deriving Typeable
data Beta = Beta Int deriving Typeable
data Gamma = Gamma Int Int deriving Typeable
data Delta = Delta Int deriving Typeable
instance Tlike Alpha
instance Tlike Beta
instance Tlike Gamma
instance Tlike Delta
sameK :: (Tlike t, Typeable t, Tlike t', Typeable t') => t -> t' -> Bool
sameK a b = typeOf a == typeOf b
Lösung
Sehen Sie sich das Data.Data Modul, die toConstr
Funktion im Besonderen. Zusammen mit {-# LANGUAGE DeriveDataTypeable #-}
das wird erhalten Sie einen 1-line-Lösung, die für jede Art arbeitet, die eine Instanz von Data.Data
ist. Sie müssen nicht, um herauszufinden, alle SYB müssen!
Wenn aus irgendeinem Grunde (klemmte mit Hugs?), Das keine Option ist, dann ist hier ein sehr hässlich und sehr langsam Hack. Es funktioniert nur, wenn Ihr Datentyp Show
able ist. (Zum Beispiel durch deriving (Show)
mit -, die keine Funktionstypen im Innern bedeutet, zum Beispiel)
constrT :: T -> String
constrT = head . words . show
sameK x y = constrT x == constrT y
constrT
bekommt die Stringdarstellung des äußersten Konstruktor einer T
Wert, indem sie es zeigt, hacken sie in Worte zu fassen und dann die erste bekommen. Ich gebe eine explizite Art Unterschrift, so dass Sie nicht in Versuchung sind sie auf andere Arten zu verwenden (und die Monomorphie Einschränkung zu umgehen).
Einige bemerkenswerte Nachteile:
- Dies bricht schrecklich, wenn der Typ hat Infix Bauer (wie
data T2 = Eta Int | T2 :^: T2
) - Wenn einige Ihrer Konstrukteure einen gemeinsamen Präfix haben, das wird langsamer bekommen, da ein größerer Teil der Strings verglichen werden muss.
- funktioniert nicht auf Typen mit einem benutzerdefinierten
show
, wie viele Bibliothekstypen.
Das heißt, es is Haskell 98 ... aber das ist über das einzig gute, was ich darüber sagen kann!
Andere Tipps
Eine andere Möglichkeit:
sameK x y = f x == f y
where f (Alpha _) = 0
f (Beta _) = 1
f (Gamma _ _) = 2
-- runtime error when Delta value encountered
Ein Laufzeitfehler ist nicht ideal, aber besser als leise die falsche Antwort zu geben.
Sie erhalten eine Generika-Bibliothek wie Schrott Ihren Textvorschlag aus oder Uniplatte zu tun dies in der Regel verwenden müssen.
Wenn Sie nicht wollen, werden so plump, können Sie Dave Hinton-Lösung verwenden, zusammen mit der leeren Datensatz Abkürzung:
...
where f (Alpha {}) = 0
f (Beta {}) = 1
f (Gamma {}) = 2
Sie haben also wissen nicht, wie viele args jeder Konstruktor hat. Aber es offensichtlich immer noch lässt etwas zu wünschen übrig.
In einigen Fällen „Schrott Ihrer Boilerplate“ Bibliothek Hilfe.
Sie können auf jeden Fall die Generika verwenden, um den Textvorschlag zu beseitigen. Ihr Code ist ein Lehrbuchbeispiel, warum ich (und viele andere nie verwenden, um die _
Wildcard auf oberster Ebene). Während es mühsam ist, alle Fälle zu schreiben, es ist weniger ermüdend als mit dem Bugs zu tun.
In diesem glücklichen Beispiel würde ich nicht nur Dave Hinton-Lösung verwenden, aber würde einen INLINE Pragma auf der Hilfsfunktion f
schlagen.