我在尝试使用Haskell的Data和Typeable,并且在没有上下文中提供类型变量的情况下,我被困在试图获取函数的参数。
让我澄清一下我的意思。只要我像下面这样量化了类型变量a,我就可以使用fromConstr并获得我想要的DataType或TypeRep列表:
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(我知道undefined和fromConstr是不完整的,但是懒惰在这里拯救了我们。)
但是,如果我试图避免对a进行量化,我就不能再对fromConstr的结果进行类型归属。我想知道是否有一种方法可以编写具有以下类型签名的函数:
constrArgs' :: Constr -> [DataType]我的最终目标是编写一个函数,该函数提供一个DataType列表,每个构造函数都有一个子列表,每个子列表都包含该构造函数的参数类型。使用第一个版本,使用类型签名来编写函数并不困难:(省略了定义)
allConstrArgs :: forall a. Data a => [[DataType]]这样做的问题是,我不能将allConstrArgs应用于其本身的结果,因为无法从DataType转换为类型级值。
那么,为了修正这一点,我们可以编写一个具有以下类型的函数吗?
allConstrsArgs' :: DataType -> [[DataType]]我在基础库中查看了一下,但我不知道如何才能实现这一点。
发布于 2019-04-16 14:01:53
您不能从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将始终为undefined,并且从未实际计算过,所以这没问题。因此,值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, AllowAmbiguousTypes, TypeApplications
https://stackoverflow.com/questions/55700144
复制相似问题