2010-07-05 64 views

回答

12

这是类似丹尼尔·普拉特的答案,但更典型的方法是放弃数据定义的类型约束,如下所示:

data SomeData a = SomeValue 
       | SomeConstructor a 

相反,您应该将(Integral a)约束放在任何需要它的函数上,即使您也将约束添加到数据定义中,您仍然必须执行该约束。对数据定义施加限制并不会带来任何收益,但会迫使您对SomeData的所有用途施加约束,即使那些根本不在意a的应用也是如此。有关更多信息,请参阅Chapter 10 of Real World Haskell

+0

请注意,与普通数据声明的类约束不同,GADT *会提供类约束,因此您不必将其明确地放在函数上。 – 2010-07-05 22:12:49

+0

GADT与限制数据定义类型的其他方式一样,通常不会以这种方式使用。一般来说,如果您只想限制这些值的类型,那么通常会更灵活,并且便于使用它的函数。 对于相反的情况,GADT更有用 - 当您需要*增加* 可以作为参数出现的类型,以及与此相关的模式匹配。 – Yitz 2010-07-06 08:11:15

+0

@Antal S-Z:我猜你被数据SomeClass a => SomeData a where SomeData :: a - > SomeData a'和'data SomeData a where SomeData :: SomeClass a => a - > SomeData a'所迷惑。在第一种情况下,您还需要添加上下文'f :: SomeClass a => SomeData a',以便在多态类型'SomeData a'的声明中使用'a'。 – ony 2010-07-06 14:01:46

6

例如,使用GADTs:

{-# LANGUAGE GADTs #-} 
data SomeData 
    where 
    SomeValue :: SomeData 
    SomeConstructor :: Integral a => a -> SomeData 

使用示例:

*Main> :t SomeValue 
SomeValue :: SomeData 

*Main> :t SomeConstructor 15 
SomeConstructor 15 :: SomeData 

*Main> :t SomeConstructor "aaa" 

<interactive>:1:0: 
    No instance for (Integral [Char]) 
     arising from a use of `SomeConstructor' at <interactive>:1:0-20 
    Possible fix: add an instance declaration for (Integral [Char]) 
    In the expression: SomeConstructor "aaa" 

*Main> let x = SomeConstructor 15 in case x of { SomeConstructor p -> fromIntegral p :: Int } 
15 
+0

没有必要GADT那里,我猜。他们更有用'数据OtherData a其中{IntData :: Integral a => a - > OtherData a; EmptyData :: OtherData EmptyType'。即当你需要通过不同的构造函数产生一些特定的(有时派生的)类型的时候。 – ony 2010-07-06 06:52:24

0

你可以做这样的事情:

data Integral a => SomeData a = 
    SomeValue 
    | SomeConstructor a 
+0

当然,这取决于OP真正想要达到的目标。您的SomeData参数化类型为'a',而他的不是。此外,它看起来像他想对不同的构造函数应用不同的限制。 – 2010-07-05 20:46:57

+0

如果SomeData不想被参数化,你可以将SomeValue拖出一个单独的类型,并通过它们加入它们,不是吗? – 2010-07-05 20:55:01

+0

再一次,如果你这样做了,你可以通过'a'类型参数化。例如,您将无法拥有由不同积分制成的SomeData组成的列表。 未参数化的容器是“多态”容器 - 您可以放置​​任何东西(它遵守其限制),并且不会影响其类型。 – 2010-07-05 21:11:47

2

是的,完全按照你想要的,但需要一提的量化: