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
War es hilfreich?

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 Showable 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.

http://www.haskell.org/haskellwiki/Scrap_your_boilerplate

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top