首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Data和Typeable获取构造函数的参数类型

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

Stack Overflow用户
提问于 2019-04-16 11:29:09
回答 1查看 201关注 0票数 3

我在尝试使用Haskell的DataTypeable,并且在没有上下文中提供类型变量的情况下,我被困在试图获取函数的参数。

让我澄清一下我的意思。只要我像下面这样量化了类型变量a,我就可以使用fromConstr并获得我想要的DataTypeTypeRep列表:

代码语言:javascript
复制
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的结果进行类型归属。我想知道是否有一种方法可以编写具有以下类型签名的函数:

代码语言:javascript
复制
constrArgs' :: Constr -> [DataType]

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

代码语言:javascript
复制
allConstrArgs :: forall a. Data a => [[DataType]]

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

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

代码语言:javascript
复制
allConstrsArgs' :: DataType -> [[DataType]]

我在基础库中查看了一下,但我不知道如何才能实现这一点。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-16 14:01:53

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

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

代码语言:javascript
复制
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将始终为undefined,并且从未实际计算过,所以这没问题。因此,值D充当类型的值级表示,这样在解构时,您就可以在作用域中获得该类型的Data实例。

函数constrArgs接受类型表示D和构造函数Constr,并返回该构造函数的参数列表,每个参数也表示为D -所以现在可以将其输出反馈到其输入!它通过使用gmapQ来实现这一点,它的第一个参数类型非常适合D构造函数。

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

下面是用法:

代码语言:javascript
复制
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, AllowAmbiguousTypes, TypeApplications

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55700144

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档