Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
获取Haskell中类型类中的实例列表_Haskell_Introspection - Fatal编程技术网

获取Haskell中类型类中的实例列表

获取Haskell中类型类中的实例列表,haskell,introspection,Haskell,Introspection,有没有一种方法可以通过编程方式获取类型类的实例列表 我觉得编译器必须知道这些信息才能进行类型检查和编译代码,所以有没有办法告诉编译器:嘿,你知道那个类的那些实例,请在这里列出它们(作为字符串或它们的任何表示形式)。我想,这是不可能的。我向您解释了typeclass(用于GHC)的实现,从中可以看出,编译器不需要知道哪些类型是typeclass的实例。它只需要知道特定类型是否为实例 typeclass将被转换为数据类型。作为一个例子,让我们以Eq: class Eq a where (==),

有没有一种方法可以通过编程方式获取类型类的实例列表


我觉得编译器必须知道这些信息才能进行类型检查和编译代码,所以有没有办法告诉编译器:嘿,你知道那个类的那些实例,请在这里列出它们(作为字符串或它们的任何表示形式)。

我想,这是不可能的。我向您解释了typeclass(用于GHC)的实现,从中可以看出,编译器不需要知道哪些类型是typeclass的实例。它只需要知道特定类型是否为实例

typeclass将被转换为数据类型。作为一个例子,让我们以
Eq

class Eq a where
  (==),(/=) :: a -> a -> Bool
typeclass将被翻译成一种字典,包含其所有功能:

data Eq a = Eq {
    (==) :: a -> a -> Bool,
    (/=) :: a -> a -> Bool
  }
然后将每个typeclass约束转换为包含字典的额外参数:

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
就足够了。

对现有类自动执行此操作是不可能的。对于您自己的类及其实例,您可以这样做。您需要通过模板Haskell(或者准引用)声明所有内容,它会自动生成一些奇怪的数据结构,对声明的实例进行编码。定义奇怪的数据结构并让模板Haskell这样做,这些细节留给任何有用例的人


也许您可以在构建中添加一些模板Haskell或其他魔法,将所有源文件作为运行时可用的文本(c.f.程序quine)包含在内。然后您的程序将“grep self”…

一旦您得到如下实例声明,就会遇到很多问题

instance Eq a => Eq [a] where
    [] == [] = True
    (x:xs) == (y:ys) = x == y && xs == ys
    _ == _ = False

以及单个具体实例(例如,
实例Eq Bool

您将获得无限多的实例列表,用于
Eq
-
Bool
[[Bool]]
[[Bool]]]
等等,
(Bool,Bool)
((Bool,Bool),Bool)
((Bool,Bool),Bool)
等等,以及这些实例的各种组合,如
((Bool,Bool.),[Bool]),Bool)
等等。不清楚如何在
字符串中表示这些;即使是
TypeRep
的列表也需要一些非常聪明的枚举

编译器可以(尝试)推断某个类型是否是任何给定类型的
Eq
实例,但它不会读入范围中的所有实例声明,然后只是开始推断所有可能的实例,因为这永远不会结束


当然,重要的问题是,你需要这个做什么

请参阅haskell文档模板:


使用
reify
,您可以获得一条信息记录,其中包含类的实例列表。您还可以直接使用
isClassInstance
classInstances

您可以使用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显示给定类型类范围内的所有实例

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,那么您也会看到所有这些实例。

实际上,编译器不知道定义了哪些实例。它唯一能做的就是检查一个类型是否是一个typeclass的实例。编译器只需要知道特定类型是否为实例,但在任何时候,编译器都可以枚举作用域中的所有实例并生成所需的列表。例如,如果您使用
:info Num
,GHCi将执行此操作。这似乎仅适用于GHC 7和TH 2.5。在TH2.4.0.1中尝试这样做似乎并没有给我想要的东西。这是新功能吗?我正在运行GHC 7.6.3。下面是答案的更新:
[ClassInstance]
现在是
[InstanceDec]
。此外,我还需要在GHCi中运行
:set-XTemplateHaskell
,以复制该示例。
*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]}]"
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