因此,如果我有两种基本相同的数据类型,我可以这样编写它们:
data A t = A1 | A2 | A3 | A4 (B t)
data B t = B1 | B2 | B3 | B4 t
type AX = A X
type AY = A Y
现在可以很容易地在 AX 和 AY 上编写函数,或者如果他们不关心的话也可以在“A t”上编写函数。但那么我该如何编写转换呢?
convert :: AX -> AY
convert (A4 (B4 x)) = A4 (B4 (xToY x))
convert ax = ay -- type error
所以现在我需要手动写出所有其他情况,即使它们都不依赖于类型参数。更糟糕的是,虽然我可以在不依赖参数的情况下使用“A {}”来匹配构造函数,但如果我需要这些参数来重建数据,则这是不可能的。
有更好的方法吗?我觉得 GADT 应该能够表达这一点,但很难看出如何从不依赖于它的术语中消除类型变量。我想我必须为 A1 A2 等设置单独的类型,然后我就会失去封闭性和大小写检查......此外我不想手动编写 Show、Eq 和 Typeable!我能想到的唯一其他方法是重新安排整个结构以隔离变化的部分,即
data A t = Independent | B4 t
data Independent = A1 | A2 | ...
但这使得数据的其他所有用途都难以利用一个转换函数。
当然,另一种选择是忘记类型安全并包含“B4(X Y)”,并在其值错误时添加一些不错的运行时错误。
也许有更好的方法来处理“几乎相同”的数据类型?
更新:所以我通过编写伪 fmap 来解决:
convert :: (a -> b) -> A a -> A b
这至少允许我将转换部分和打包-解包样板分开。我想,足够先进的 TH 可能可以为我生成其中之一。不过,我仍然对其他方法感到好奇。我觉得这种方式仍然不精确,因为它允许用任何东西来填充这个洞,而我真正的意思恰恰是两件事之一。
最佳答案
看起来基本上您想要的是 A
和 B
成为 Functor
。幸运的是,您可以允许 Haskell 使用 -XDeriveFunctor
派生这些类型的明显的 Functor
实例。然后,使用以下代码:
data A t = A1 | A2 | A3 | A4 (B t) deriving Functor
data B t = B1 | B2 | B3 | B4 t deriving Functor
...
convert :: AX -> AY
convert a = fmap xToY a
下面是一个示例 GHCi session ,添加了一些 X
、Y
和 xToY
的虚拟定义,并且所有内容都派生 显示
:
*Main> convert $ A4 B1
A4 B1
*Main> convert $ A4 (B4 X)
A4 (B4 Y)
*Main> convert $ A2
A2
更新
如果您确实只想允许 A
和 B
被 X
或 Y 类型的内容“填充”
,你可以像这样约束它们:
class XorY t where
instance XorY X
instance XorY Y
data XorY t => A t = A1 | A2 | A3 | A4 (B t)
data XorY t => B t = B1 | B2 | B3 | B4 t
当然,这将排除制作 A
和 B
Functor
的可能性,因此上面概述的技巧将不再起作用.
关于haskell - 有漏洞的数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7185366/