Es ist nicht möglich zu prüfen, ob ein Typ zur Laufzeit eine Instanz von etwas ist. Einer der Gründe ist, dass das Importieren eines Moduls neue Instanzen einbringen könnte und das Ergebnis der Funktion ändern würde (dies wäre sehr schlecht für Haskell).
Sie können durch eine Liste bekannter Typen gehen. Die Idee ist, dass Sie den Typ zusammen mit den Instanzen für diesen Typ in einer GADT
speichern und darauf abgleichen, um die gewünschten Instanzen zu erhalten. Ich bin mir nicht ganz sicher, was Sie wollen, aber ich denke, es ist so etwas wie das ist:
data EqShow where
JustEq :: (Typeable a, Eq a) => Proxy a -> EqShow
EqShow :: (Typeable a, Eq a, Show a) => Proxy a -> EqShow
Es gibt eine Version für die Typen, die nur Eq
und eine haben, dass beide Eq
und Show
haben. Die Idee ist, dass wir auf diese passen können, wenn die Typen übereinstimmen und die Instanz Eq
verwenden, um zu überprüfen, ob sie gleich sind. Wenn eine Show
Instanz verfügbar ist, können wir auch die Ergebnisse anzeigen. Um mehrere EqShows zu speichern, habe ich eine Hash-Map verwendet, um die Typen zu suchen. Hier ist der vollständige Code:
{-# LANGUAGE GADTs #-}
import Data.Dynamic
import Data.Typeable
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as HM
data EqShow where
JustEq :: (Typeable a, Eq a) => Proxy a -> EqShow
EqShow :: (Typeable a, Eq a, Show a) => Proxy a -> EqShow
justEq :: (Typeable a, Eq a) => Proxy a -> (TypeRep, EqShow)
justEq p = (typeRep p, JustEq p)
eqShow :: (Typeable a, Eq a, Show a) => Proxy a -> (TypeRep, EqShow)
eqShow p = (typeRep p, EqShow p)
-- | Different outcomes of testing equality.
data Result
= DifferentType TypeRep TypeRep
| NotEq TypeRep (Maybe (String, String))
| IsEq TypeRep (Maybe String)
| UnknownType TypeRep
deriving Show
-- | Check if two Dynamics are equal. Show them if possible
dynEq :: HashMap TypeRep EqShow -> Dynamic -> Dynamic -> Result
dynEq hm da db
| ta /= tb = DifferentType ta tb
| otherwise =
case HM.lookup ta hm of
Just (EqShow p) -> checkEqShow p (fromDynamic da) (fromDynamic db)
Just (JustEq p) -> checkJustEq p (fromDynamic da) (fromDynamic db)
Nothing -> UnknownType ta
where
ta = dynTypeRep da
tb = dynTypeRep db
-- Check if two results are equal and display them.
checkEqShow :: (Typeable a, Eq a, Show a) => Proxy a -> Maybe a -> Maybe a -> Result
checkEqShow _ (Just a) (Just b)
| a == b = IsEq ta (Just (show a))
| otherwise = NotEq ta (Just (show a, show b))
checkEqShow _ _ _ = UnknownType ta
-- Check if two results are equal without displaying them.
checkJustEq :: (Typeable a, Eq a) => Proxy a -> Maybe a -> Maybe a -> Result
checkJustEq p (Just a) (Just b)
| a == b = IsEq ta Nothing
| otherwise = NotEq ta Nothing
checkJustEq p _ _ = UnknownType ta
Sie können sie eine Liste der bekannten Typen machen:
knownEqShows :: HashMap TypeRep EqShow
knownEqShows = HM.fromList
[ eqShow (Proxy :: Proxy Int)
, eqShow (Proxy :: Proxy Char)
]
und überprüfen Sie sie:
> let a = toDyn 'a'
> let b = toDyn 'b'
> let c = toDyn (1 :: Int)
> dynEq knownEqShows a a
IsEq Char (Just "'a'")
> dynEq knownEqShows a b
NotEq Char (Just ("'a'","'b'"))
> dynEq knownEqShows a c
DifferentType Char Int
Gene bekannt EqShow
s Template Haskell verwenden wäre schwierig, . Sie könnten vielleicht eine Version für Typen ohne Variablen erstellen (Double
, Char
usw.). Wenn Sie jedoch eine Variable haben (Maybe a
zum Beispiel), könnten Sie dies nicht in EqShow speichern, Sie müssten alle Versionen schreiben davon (Maybe Int
, Maybe (Maybe Double)
usw.), aber es gibt eine unendliche Anzahl von diesen.
Natürlich wäre es viel einfacher, anstatt Dynamic
der Verwendung eines anderen Wrapper zu verwenden (möglich):
data EqShowDynamic where
JustEqD :: (Typeable a, Eq a) => a -> EqShowDynamic
EqShowD :: (Typeable a, Eq a, Show a) => a -> EqShowDynamic
So sind die Eq
und Show
Instanzen bereits vorhanden sind.
Ich bin neugierig auf Ihren Anwendungsfall hier. Warum arbeitest du überhaupt mit "Dynamic"? –
@BenjaminHodgson Ich arbeite an einem Interpreter für eine objektorientierte Sprache und möchte die Einbettung beliebiger Haskell-Werte in einen eigenen Wertetyp erlauben, damit Objektmethoden, die als Haskell-Code implementiert sind, alle benötigten Werte in den Objekten des Zielsprache. – Jules