一个属于两种不同类型的值构造函数

yon*_*alf 3 haskell types

假设我有三个值构造函数:

A { a :: Int }
B { b :: Char }
C { c :: Bool }
Run Code Online (Sandbox Code Playgroud)

我想创建两种类型X,Y这样类型的值X可以是A,B或者C,像这样:

data X = A {...} | B {...} | C {...}
Run Code Online (Sandbox Code Playgroud)

而type的值Y只能是一个AB类似的东西:

data Y = A {...} | B {...}
Run Code Online (Sandbox Code Playgroud)

这样我就可以编写这样的代码:

foo :: X -> Int -- can pattern match
foo (A _) = 1
foo (B _) = 2
foo (C _) = 3
bar :: Y -> Bool -- also can pattern match with the same constructors
bar (A _) = true
bar (B _) = false
baz = A 1 -- baz is inferred to be a type that can fit in both X and Y
Run Code Online (Sandbox Code Playgroud)

我知道我可以将构造函数包装在定义中X并且Y像这样:

data X = XA A | XB B | XC C
data Y = YA A | YB B
Run Code Online (Sandbox Code Playgroud)

但这似乎不整洁(必须一直打字XA A等).我可以扩大的内容A,B以及C到的定义XY,但A等是相当复杂的,我宁愿不要重复定义.

这对Haskell来说是否可行,包括任何GHC扩展?

编辑

似乎GADT可以按照要求回答我的问题(因此我将散热片的答案标记为正确),但仍然不够灵活,无法满足我的需求.例如,据我所知,你做不到这样的事情:

func1 :: [XY Y_] -- returns a list of items that can only be A or B
func1 = ...
func2 = func1 ++ [C True] -- adding a C item to the list
Run Code Online (Sandbox Code Playgroud)

func2应该输入[XY X_],但这在Haskell中是不可能的(除非我的实验是错误的).

经过更多的网络搜索,我真正想要的是OCaml的多态变体(据我所知)仅存在于OCaml中(看起来更"实用"而不是"学术"语言).

编辑2

请参阅comonad的回答.它似乎真的可以做到,但我想我最好不要多次重写这个问题.:-)

Hea*_*ink 7

正如jetxee所描述的那样,类型类可能是合适的方法.

如果您还希望能够模式匹配并使用构造函数,那么您可以使用GADT和空数据声明来定义一种数据类型中的所有构造函数.如果采用此方法,则所有构造函数都将是相同数据类型的成员,同时允许您将域限制为仅构造函数的子集.

data X_
data Y_
data XY a where
  A :: Int -> XY a
  B :: Char -> XY a
  C :: Bool -> XY X_
type X = XY X_ -- Contains values built with constructors A, B, and C 
type Y = XY Y_ -- Contains only values built with constructors A and B
Run Code Online (Sandbox Code Playgroud)

现在只有一个使用功能AB两种类型的作品XY.使用的函数C仅适用于类型X.


sep*_*p2k 5

baz = A 1 -- baz is inferred to be a type that can fit in both X and Y
Run Code Online (Sandbox Code Playgroud)

这将要求Haskell支持某种形式的子类型,但它不支持.没有ghc扩展可以实现这一点.

你可以做的最好的事情可能是这样的:

data Y = A ... | B ...
data X = XY Y | C ...
Run Code Online (Sandbox Code Playgroud)

这样你就不必重复构造函数A而且B你不必编写Y (A foo)任何一个 - 你可以编写A foo以获得类型的值Y.

但是,您必须编写X (A foo)以获取X包含的类型的值A.这不是你想要的,但是你会得到最接近的,我害怕.