假设我有三个值构造函数:
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只能是一个A或B类似的东西:
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到的定义X和Y,但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的回答.它似乎真的可以做到,但我想我最好不要多次重写这个问题.:-)
正如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)
现在只有一个使用功能A和B两种类型的作品X和Y.使用的函数C仅适用于类型X.
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.这不是你想要的,但是你会得到最接近的,我害怕.