带孔的数据类型

时间:2011-08-25 05:01:17

标签: haskell types abstract-data-type

因此,如果我有两种大致相同的数据类型,我可以像这样编写它们:

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可能会为我产生其中一个。我仍然对其他方法感到好奇。我觉得这种方式仍然不准确,因为它允许用任何填充洞,而我真正的意思是两件事之一。

1 个答案:

答案 0 :(得分:2)

基本上你想要的是AB Functor。幸运的是,您可以使用Functor允许Haskell为这些类型派生明显的-XDeriveFunctor实例。然后,使用以下代码:

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

以下是为XYxToY添加一些虚拟定义后的GHCi会话示例,以及导出Show的所有内容:

*Main> convert $ A4 B1
A4 B1
*Main> convert $ A4 (B4 X)
A4 (B4 Y)
*Main> convert $ A2
A2

更新

如果您真的只想让AB“填充”XY类型的内容,您可以这样约束它们:< / p>

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

当然,这将排除制作AB Functor的可能性,因此上述技巧将不再适用。