有没有办法以编程方式获取类型类的实例列表?
让我觉得编译器必须知道这些信息才能输入检查和编译代码,所以有没有办法告诉编译器:嘿,你知道那个类的那些实例,请把它们放在正确的列表中在这里(作为字符串或其他任何表示)。
答案 0 :(得分:13)
您可以使用Template Haskell为给定类型类生成范围内的实例。
import Language.Haskell.TH
-- get a list of instances
getInstances :: Name -> Q [ClassInstance]
getInstances typ = do
ClassI _ instances <- reify typ
return instances
-- convert the list of instances into an Exp so they can be displayed in GHCi
showInstances :: Name -> Q Exp
showInstances typ = do
ins <- getInstances typ
return . LitE . stringL $ show ins
在GHCi中运行:
*Main> $(showInstances ''Num)
"[ClassInstance {ci_dfun = GHC.Num.$fNumInteger, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Integer.Type.Integer]},ClassInstance {ci_dfun = GHC.Num.$fNumInt, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Types.Int]},ClassInstance {ci_dfun = GHC.Float.$fNumFloat, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Types.Float]},ClassInstance {ci_dfun = GHC.Float.$fNumDouble, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Types.Double]}]"
另一个有用的技术是使用GHCi显示给定类型类的范围内的所有实例。
Prelude> :info Num
class (Eq a, Show a) => Num a where
(+) :: a -> a -> a
(*) :: a -> a -> a
(-) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
-- Defined in GHC.Num
instance Num Integer -- Defined in GHC.Num
instance Num Int -- Defined in GHC.Num
instance Num Float -- Defined in GHC.Float
instance Num Double -- Defined in GHC.Float
编辑:重要的是要知道编译器只知道任何给定模块(或者在ghci提示符等)的范围内的类型类。因此,如果您在没有导入的情况下调用showInstances
TH函数,则只能从Prelude中获取实例。如果您在范围内有其他模块,例如Data.Word,那么你也会看到所有这些实例。
答案 1 :(得分:7)
请参阅模板haskell文档:http://hackage.haskell.org/packages/archive/template-haskell/2.5.0.0/doc/html/Language-Haskell-TH.html
使用reify
,您可以获得一条信息记录,该记录包含一个类的实例列表。您也可以直接使用isClassInstance
和classInstances
。
答案 2 :(得分:6)
一旦获得像
这样的实例声明,就会遇到很多问题instance Eq a => Eq [a] where
[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
_ == _ = False
和
instance (Eq a,Eq b) => Eq (a,b) where
(a1,b1) == (a2,b2) = a1 == a2 && b1 == b2
以及单个具体实例(例如instance Eq Bool
)。
您将获得Eq
- Bool
,[Bool]
,[[Bool]]
,[[[Bool]]]
等实例的无限列表,{{1} },(Bool,Bool)
,((Bool,Bool),Bool)
等,以及这些的各种组合,例如(((Bool,Bool),Bool),Bool)
等。目前尚不清楚如何在([((Bool,[Bool]),Bool)],Bool)
中表示这些内容;即使是String
列表也需要一些非常聪明的枚举。
编译器可以(尝试)推断出某个类型是否是任何给定类型的TypeRep
的实例,但它不读取范围内的所有实例声明然后刚开始推断所有可能的实例,因为那将永远不会完成!
重要的问题当然是,你需要什么呢?
答案 3 :(得分:2)
我想,这是不可能的。我解释了类型类(对于GHC)的实现,从中可以看出,编译器不需要知道哪些类型是类型类的实例。它只需知道特定类型是否为实例。
类型类将被转换为数据类型。举个例子,我们来看Eq
:
class Eq a where
(==),(/=) :: a -> a -> Bool
类型类将被翻译成一种包含其所有功能的字典:
data Eq a = Eq {
(==) :: a -> a -> Bool,
(/=) :: a -> a -> Bool
}
然后将每个类型类约束转换为包含字典的额外参数:
elem :: Eq a => a -> [a] -> Bool
elem _ [] = False
elem a (x:xs) | x == a = True
| otherwise = elem a xs
变为:
elem :: Eq a -> a -> [a] -> Bool
elem _ _ [] = False
elem eq a (x:xs) | (==) eq x a = True
| otherwise = elem eq a xs
重要的是,字典将在运行时传递。想象一下,您的项目包含许多模块。 GHC不必检查实例的所有模块,只需要查找,是否在任何地方定义了实例。
但是如果您有可用的源代码,我想这些实例的旧式grep
就足够了。
答案 4 :(得分:0)
无法为现有类自动执行此操作。对于你自己的班级和实例,你可以做到。您需要通过Template Haskell(或者可能是准引用)声明所有内容,它会自动生成一些编码声明实例的奇怪数据结构。定义奇怪的数据结构并使模板Haskell执行此操作是留给任何具有用例的用户的详细信息。
也许您可以在构建中添加一些模板Haskell或其他魔法,以将所有源文件包含为运行时可用的文本(c.f。program quine)。那么你的程序就会'自我'......