無法在運行時檢查某個類型是否是某個實例的實例。其中一個原因是導入一個模塊會引入新的實例,這會改變函數的結果(這對Haskell來說是非常糟糕的)。
你可以通過已知類型的列表。這個想法是你將該類型與該類型的實例一起存儲在GADT
中,並匹配它以獲取所需的實例。我不太確定你想要什麼,但我認爲這是這樣的:
data EqShow where
JustEq :: (Typeable a, Eq a) => Proxy a -> EqShow
EqShow :: (Typeable a, Eq a, Show a) => Proxy a -> EqShow
有對類型的版本只有Eq
和一個同時具有Eq
和Show
。我們的想法是,如果類型匹配,並使用Eq
實例來檢查它們是否相等,我們可以匹配這些類型。如果有一個Show
實例可用,我們也可以顯示結果。爲了存儲多個EqShows,我使用了一個哈希映射,以便我們可以查找這些類型。下面是完整的代碼:
{-# 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
您可以將它們製作已知類型的列表:
knownEqShows :: HashMap TypeRep EqShow
knownEqShows = HM.fromList
[ eqShow (Proxy :: Proxy Int)
, eqShow (Proxy :: Proxy Char)
]
,並檢查他們:
> 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
生成稱爲EqShow
S使用模板哈斯克爾將是困難。您可以爲不帶變量的類型創建版本(Double
,Char
等)但是如果您有一個變量(例如Maybe a
),您將無法將其存儲在EqShow中,那麼您必須編寫所有版本(Maybe Int
,Maybe (Maybe Double)
等),但有無數的這些。
當然,這將是非常容易的,而不是使用Dynamic
使用另一種包裝(有可能):
data EqShowDynamic where
JustEqD :: (Typeable a, Eq a) => a -> EqShowDynamic
EqShowD :: (Typeable a, Eq a, Show a) => a -> EqShowDynamic
所以Eq
和Show
實例已經存在。
我很好奇你的用例在這裏。你爲什麼首先使用'Dynamic'? –
@BenjaminHodgson我正在開發面嚮對象語言的解釋器,並希望允許將任意Haskell值嵌入到其自己的值類型中,以允許以Haskell代碼實現的對象方法將所需的任何值存儲在目標語言。 – Jules