【问题标题】:Dynamically get the list of data constructors of a type动态获取某个类型的数据构造函数列表
【发布时间】:2016-09-11 23:09:21
【问题描述】:

我有这样的数据类型:

data ABCS = A Int | B Int | ... | Z Int deriving (Data, Typeable)

在测试中,我想动态提取所有构造函数,从每个构造函数中创建一个实例,然后运行测试。

我一直在查看 Data.TypeableData.Data,但我还没有看到/理解如何仅从类型 (ABC) 开始执行此操作。

非常感谢您的帮助。

【问题讨论】:

  • Arbitrary 是否已经做你想做的事了(不诉诸Data.Data)?否则,您希望向构造函数提供哪些参数?
  • 基本上我想将数字列表与构造函数列表一起加入。但我想动态获取构造函数列表。
  • Arbitrary 在这种情况下对我没有帮助。我不希望随机生成数据。我想为每个数据实例测试一个条件。
  • 您可能还想查看smallcheck 用于生成测试用例的任何内容。与 quickcheck 不同,它专为详尽的测试而设计。
  • @dfeuer 我认为答案可能是实际使用 smallcheck!谢谢推荐。

标签: haskell reflection types


【解决方案1】:

如果您可以使用Data.Data,它适用于这个用例,但由于Int 参数,它有点笨拙。

{-# LANGUAGE ScopedTypeVariables #-}
import Data.Data
import Data.Typeable

allCtors :: forall a. Data a => [Int -> a]
allCtors = map observeCtor $ dataTypeConstrs $ dataTypeOf (undefined :: a)
  where
    observeCtor :: Constr -> Int -> a
    observeCtor c i = fromJust $ fromConstrM (cast i) c

然后我们有例如

λ data ABC = A Int | B Int | C Int deriving (Show, Data, Typeable)
data ABC = A Int | B Int | C Int
λ map ($ 2) allCtors :: [ABC]
[A 2,B 2,C 2]

如果您不想使用Data.Data,您可以使用GHC.Generics-XDefaultSignatures 来做到这一点


FWIW,如果您可以重构 ABC 以便 A、B、C 标记是它们自己的类型,那么您就不必处理任何这些...

data ABCTagged = ABCTagged ABC Int deriving Show

data ABC = A | B | C deriving (Show, Eq, Ord, Enum. Bounded)

...然后只需使用enumFrom minBound :: [ABC] 即可获取整个列表。简单的!不过,不确定这对您来说是否可行。

【讨论】:

  • 数据类型不是我的实际数据类型。这是我能做的最简单的例子,它可以表达我想要完成的事情。所以,标记的例子对我不起作用。不过谢谢你的建议。
  • 为了我自己的理解,我正在逐个分解你的答案,当我执行这个fromConstrM (Just 10 :: Maybe Int) ctor我得到Couldn't match type ‘d’ with ‘Int’ ‘d’ is a rigid type variable bound by a type expected by the context: forall d. Data d => Maybe d at <interactive>:52:1 Expected type: Maybe d Actual type: Maybe Int在这种情况下,ctorA确实持有一个有效的@987654332 @构造函数show ctorA -> "A"
  • 好的,我已经部分弄清楚了:let c = cast 10 :: forall d. Data d => Maybe d 然后fromConstrM c ctorA 但我得到了Just ()
  • 你的想法是对的。 cast 10 :: forall d. Data d => Maybe d几乎可以工作,但 10 的类型将默认为 Integer,所以当我们尝试将其转换为 Int 时,我们会遇到问题。问题 #2 是,当您调用 fromConstrM 时,您需要更具体地了解类型,否则您将获得 forall d. Data d => Maybe d 并且 GHCi 已经足够好,可以默认为您使用 d ~ ()。所以完整的咒语应该是fromConstrM (cast (10 :: Int)) ctorA :: Maybe ABC
猜你喜欢
  • 2012-09-29
  • 1970-01-01
  • 2017-09-14
  • 2017-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
相关资源
最近更新 更多