gpt4 book ai didi

haskell - 如何在运行时读取类型的元数据?

转载 作者:行者123 更新时间:2023-12-03 14:01:34 25 4
gpt4 key购买 nike

我想编写一个程序,打印出一些 Haskell 类型的元数据。虽然我知道这不是有效的代码,但这个想法是这样的:

data Person = Person { name :: String, age :: Int }

metadata :: Type -> String
metadata t = ???

metadata Person -- returns "Person (name,age)"

重要的限制是我没有 Person 的实例,只是类型。

我已经开始研究泛型和可类型/数据,但没有实例我不确定他们会做我需要的。谁能指出我正确的方向?

最佳答案

Haskell 中的反射使用 Typeable类,在 Data.Typeable 中定义并包括 typeOf * 获取值类型的运行时表示的方法。

ghci> :m +Data.Typeable
ghci> :t typeOf 'a'
typeOf 'a' :: TypeRep
ghci> typeOf 'a' -- We could use any value of type Char and get the same result
Char -- the `Show` instance of `TypeRep` just returns the name of the type

如果你想要 Typeable要为您自己的类型工作,您可以让编译器使用 DeriveDataTypeable 为您生成一个实例扩大。
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
data Person = Person { name :: String, age :: Int } deriving Typeable

您也可以编写自己的实例,但实际上,没有人有时间这样做。显然你不能 - 查看评论

您现在可以使用 typeOf获取您的类型的运行时表示。我们可以查询类型构造函数(缩写为 TyCon )及其类型参数的信息:
-- (undefined :: Person) stands for "some value of type Person".
-- If you have a real Person you can use that too.
-- typeOf does not use the value, only the type
-- (which is known at compile-time; typeOf is dispatched using the normal instance selection rules)
ghci> typeOf (undefined :: Person)
Person
ghci> tyConName $ typeRepTyCon $ typeOf (undefined :: Person)
"Person"
ghci> tyConModule $ typeRepTyCon $ typeOf (undefined :: Person)
"Main"
Data.Typeable还提供了一个类型安全的转换操作,允许您在值的运行时类型上进行分支,有点像 C# 的 as运算符(operator)。
f :: Typeable a => a -> String
f x = case (cast x :: Maybe Int) of
Just i -> "I can treat i as an int in this branch " ++ show (i * i)
Nothing -> case (cast x :: Maybe Bool) of
Just b -> "I can treat b as a bool in this branch " ++ if b then "yes" else "no"
Nothing -> "x was of some type other than Int or Bool"

ghci> f True
"I can treat b as a bool in this branch yes"
ghci> f (3 :: Int)
"I can treat i as an int in this branch 9"

顺便说一句,写 f 的更好方法是使用 GADT 枚举您希望调用函数的类型集。这让我们失去了 Maybe ( f 永远不会失败!),更好地记录我们的假设,并在我们需要更改 f 的可接受参数类型集时提供编译时反馈. (如果你愿意,你可以编写一个类来使 Admissible 隐含。)
data Admissible a where
AdInt :: Admissible Int
AdBool :: Admissible Bool
f :: Admissible a -> a -> String
f AdInt i = "I can treat i as an int in this branch " ++ show (i * i)
f AdBool b = "I can treat b as a bool in this branch " ++ if b then "yes" else "no"

实际上,我可能不会做这任何一个 - 我会坚持 f在一个类中并为 Int 定义实例和 Bool .

如果您想要关于类型定义右侧的运行时信息,您需要使用有趣命名的 Data.Data ,它定义了 Typeable 的子类调用 Data .** GHC 可以推导出 Data对你也一样,扩展名相同:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
import Data.Data
data Person = Person { name :: String, age :: Int } deriving (Typeable, Data)

现在我们可以获取类型值的运行时表示,而不仅仅是类型本身:
ghci> dataTypeOf (undefined :: Person)
DataType {tycon = "Main.Person", datarep = AlgRep [Person]}
ghci> dataTypeConstrs $ dataTypeOf (undefined :: Person)
[Person] -- Person only defines one constructor, called Person
ghci> constrFields $ head $ dataTypeConstrs $ dataTypeOf (undefined :: Person)
["name","age"]
Data.Data是泛型编程的 API;如果你听到人们谈论 "Scrap Your Boilerplate" ,这(以及建立在 Data.Generics 之上的 Data.Data )就是他们的意思。例如,您可以编写一个函数,使用类型字段上的反射将记录类型转换为 JSON。
toJSON :: Data a => a -> String
-- Implementation omitted because it is boring.
-- But you only have to write the boring code once,
-- and it'll be able to serialise any instance of `Data`.
-- It's a good exercise to try to write this function yourself!

* 在 GHC 的最新版本中,此 API 有所改变。查阅文档。

** 是的,该类的完全限定名称是 Data.Data.Data .

关于haskell - 如何在运行时读取类型的元数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28243383/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com