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

发布于 2024-10-25 13:43:40 字数 129 浏览 3 评论 0原文

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

让我震惊的是,编译器必须知道这些信息才能进行类型检查和编译代码,因此有什么方法可以告诉编译器:嘿,你知道该类的那些实例,请在此处列出它们的列表(如字符串或它们的任何表示形式)。

Is there a way to programmatically get a list of instances of a type class?

It strikes me that the compiler must know this information in order to type check and compile the code, so is there some way to tell the compiler: hey, you know those instances of that class, please put a list of them right here (as strings or whatever some representation of them).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

盛夏尉蓝 2024-11-01 13:43:40

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

You can generate the instances in scope for a given type class using 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

Running this in 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]}]"

Another useful technique is showing all instances in scope for a given type class using 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

Edit: The important thing to know is that the compiler is only aware of type classes in scope in any given module (or at the ghci prompt, etc.). So if you call the showInstances TH function with no imports, you'll only get instances from the Prelude. If you have other modules in scope, e.g. Data.Word, then you'll see all those instances too.

绅刃 2024-11-01 13:43:40

请参阅模板 haskell 文档: http://hackage.haskell.org/packages/archive/template-haskell/2.5.0.0/doc/html/Language-Haskell-TH.html

使用reify,您可以获取一个信息记录,其中包含类的实例列表。您还可以直接使用 isClassInstanceclassInstances

See the template haskell documentation: http://hackage.haskell.org/packages/archive/template-haskell/2.5.0.0/doc/html/Language-Haskell-TH.html

Using reify, you can get an Info record, which for a class includes its list of instances. You can also use isClassInstance and classInstances directly.

安静 2024-11-01 13:43:40

一旦您获得实例声明

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]]] 等等,(Bool,Bool), ((Bool,Bool),Bool), (((Bool,Bool),Bool),Bool) 等等,以及这些的各种组合,例如 ([((Bool,[Bool]),Bool)],Bool)等等。目前尚不清楚如何在 String 中表示这些;即使是 TypeRep 列表也需要一些非常智能的枚举。

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

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

This is going to run into a lot of problems as soon as you get instance declarations like

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

and

instance (Eq a,Eq b) => Eq (a,b) where
    (a1,b1) == (a2,b2) = a1 == a2 && b1 == b2

along with a single concrete instance (e.g. instance Eq Bool).

You'll get an infinite list of instances for Eq - Bool,[Bool],[[Bool]],[[[Bool]]] and so on, (Bool,Bool), ((Bool,Bool),Bool), (((Bool,Bool),Bool),Bool) etcetera, along with various combinations of these such as ([((Bool,[Bool]),Bool)],Bool) and so forth. It's not clear how to represent these in a String; even a list of TypeRep would require some pretty smart enumeration.

The compiler can (try to) deduce whether a type is an instance of Eq for any given type, but it doesn't read in all the instance declarations in scope and then just starts deducing all possible instances, since that will never finish!

The important question is of course, what do you need this for?

巷雨优美回忆 2024-11-01 13:43:40

我想,这是不可能的。我向您解释了类型类的实现(对于 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 就足够了。

I guess, it's not possible. I explain you the implementation of typeclasses (for GHC), from it, you can see, that the compiler has no need to know which types are instance of a typeclass. It only has to know, whether a specific type is instance or not.

A typeclass will be translated into a datatype. As an example, let's take Eq:

class Eq a where
  (==),(/=) :: a -> a -> Bool

The typeclass will be translated into a kind of dictionary, containing all its functions:

data Eq a = Eq {
    (==) :: a -> a -> Bool,
    (/=) :: a -> a -> Bool
  }

Each typeclass constraint is then translated into an extra argument containing the dictionary:

elem :: Eq a => a -> [a] -> Bool
elem _ [] = False
elem a (x:xs) | x == a    = True
              | otherwise = elem a xs

becomes:

elem :: Eq a -> a -> [a] -> Bool
elem _  _ [] = False
elem eq a (x:xs) | (==) eq x a = True
                 | otherwise   = elem eq a xs

The important thing is, that the dictionary will be passed at runtime. Imagine, your project contains many modules. GHC doesn't have to check all the modules for instances, it just has to look up, whether an instance is defined anywhere.

But if you have the source available, I guess an old-style grep for the instances would be sufficient.

孤者何惧 2024-11-01 13:43:40

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

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

It is not possible to automatically do this for existing classes. For your own class and instances thereof you could do it. You would need to declare everything via Template Haskell (or perhaps the quasi-quoting) and it would automatically generate some strange data structure that encodes the declared instances. Defining the strange data structure and making Template Haskell do this are details left to whomever has a use case for them.

Perhaps you could add some Template Haskell or other magic to your build to include all the source files as text available at run-time (c.f. program quine). Then your program would 'grep itself'...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文