gpt4 book ai didi

haskell - 使用 Data 和 Typeable 获取构造函数的参数类型

转载 作者:行者123 更新时间:2023-12-02 13:37:13 25 4
gpt4 key购买 nike

我正在玩 Haskell 的 DataTypeable ,并且我一直试图在上下文中没有可用类型变量的情况下获取函数的参数。

让我澄清一下我的意思。只要我像下面这样对类型变量 a 进行量化,我就可以使用 fromConstr 并获取 DataTypeTypeRep< 的列表 如我所愿:

constrArgs :: forall a. Data a => Constr -> [DataType]
constrArgs c = gmapQ f (fromConstr c :: a)
where f :: forall d. Data d => d -> DataType
f _ = dataTypeOf @d undefined

(我意识到 undefinedfromConstr 并不完全,但懒惰拯救了我们。)

但是,如果我尝试避免量化 a,我就无法再对 fromConstr 的结果进行类型归因。我想知道是否有办法编写具有以下类型签名的函数:

constrArgs' :: Constr -> [DataType]

我的最终目标是编写一个函数,给出一个 DataType 列表的列表,每个构造函数的子列表,每个子列表包含该构造函数的参数类型。使用第一个版本,编写具有类型签名的函数并不困难:(省略定义)

allConstrArgs :: forall a. Data a => [[DataType]]

这样做的问题是我无法将 allConstrArgs 应用于其自身的结果,因为无法从 DataType 转换为类型级别值。

那么,为了修改它,我们可以编写一个具有以下类型的函数吗?

allConstrsArgs' :: DataType -> [[DataType]]

我环顾了基础库,但我不知道如何实现这一点。

最佳答案

您无法从 Constr 中获取参数类型列表,因为它没有足够的数据:它只是一堆字符串,仅此而已。

但是,有一种方法可以实现您的更大目标:您只需随身携带 Data 字典,还有什么比存在类型更好的方法呢!

data D = forall a. Data a => D a

allConstrArgs :: D -> [[D]]
allConstrArgs d = constrArgs d <$> allConstrs d

constrArgs :: D -> Constr -> [D]
constrArgs (D a) c = gmapQ D $ mkConstr a c
where
mkConstr :: forall a. Data a => a -> Constr -> a
mkConstr _ = fromConstr

allConstrs :: D -> [Constr]
allConstrs (D a) = case dataTypeRep $ dataTypeOf a of
AlgRep constrs -> constrs
_ -> []

mkD :: forall a. Data a => D
mkD = D (undefined :: a)

这里,类型D仅用于包装Data字典 - 实际值a将始终是未定义,并且从未实际评估过,所以没关系。因此,值D充当类型的值级表示,这样在解构时您就可以在范围内获得该类型的Data实例。

函数constrArgs采用类型表示形式D和构造函数Constr,并返回该构造函数参数的列表,每个参数表示为D 也是如此 - 所以现在您可以将其输出反馈到其输入中!它通过使用 gmapQ 来实现这一点,其第一个参数类型完全适合 D 构造函数。

mkD 只是一个实用函数,旨在隐藏 undefined 的不愉快之处,并与 TypeApplications 一起使用,例如mkD @Int

用法如下:

data X = X0 Int | X1 String deriving (Typeable, Data)
data Y = Y0 String | Y1 Bool | Y2 Char deriving (Typeable, Data)
data Z = ZX X | ZY Y deriving (Typeable, Data)

typName :: D -> String
typName (D a) = dataTypeName $ dataTypeOf a

main = do
-- Will print [["Prelude.Int"],["Prelude.[]"]]
print $ map typName <$> allConstrArgs (mkD @X)

-- Will print [["Prelude.[]"],["Bool"],["Prelude.Char"]]
print $ map typName <$> allConstrArgs (mkD @Y)

-- Will print [["X"],["Y"]]
print $ map typName <$> allConstrArgs (mkD @Z)

请注意,您需要以下扩展才能使其工作:ScopedTypeVariables、DeriveDataTypeable、GADTs、AllowAmbigouslyTypes、TypeApplications

关于haskell - 使用 Data 和 Typeable 获取构造函数的参数类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55700144/

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