如果我有类型的TypeRep
或包含该类型实例的Dynamic
,有什么方法可以找到已知类型类的相应实例,以便我可以调用函数在该类型类上,无需知道有问题的完整类型?如果没有这样的功能,是否有理由不可能,或者它没有被实现?
或者,是否有一种方法(可能使用模板haskell)运行带有类型类实例的所有类型的生成列表,以便我可以对每个类型执行动态转换并检查结果?
我要做的是实现Eq
和Show
的版本,它实际显示Dynamic
的数据(只要有可能),以便更简单,更一般地编写有用的单元测试;我并不真的需要高性能,所以使用贯穿所有可能性的生成代码是可以接受的。
答案 0 :(得分:3)
无法检查类型是否是运行时的某个实例。其中一个原因是导入模块可能会带来新的实例,这会改变函数的结果(这对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
使用模板haskell生成已知的EqShow
将很困难。您可以为没有变量的类型制作版本(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
实例已经存在。