2016-11-19 279 views
1

this问题中,未标记的联合被描述为子类型的一种形式。允许未标记的工会等同于允许类型类?

类型类也是子类型的一种形式。

它们在概念上是否相同?呃他们是,我将如何在Haskell中实现这些?

+4

我认为Haskell没有子类型,因为它会被传统定义,因为每个值都有一个_one_类型。类型类只是扩展类型语言以允许用户定义的多态性的一种方式。也就是说,它让一个值'x'具有'C a => a'的类型,其中'C'是一组规则'a'必须符合的;但是'x'仍然只有一种类型。将其与OOPy子类型相对比,其中值'x'具有多种类型:其基本类型和从基本类型中继承的任何类型。 – hao

+1

另一个更迂腐的论点:子类型会抑制Haskell编译器具有的DHM类型主体类型推断,而类型类不会。 – hao

+3

详细说明@ haoformayor的观点:不要混淆子类型和ad-hoc多态。子类型是ad-hoc多态的一种风格,类型类是一个相当不同的类型。 –

回答

4

类型类也是子类型的一种形式。

他们不是。为了说明起见,让我们回到TypeScript examples我在这个问题提到:

如果我们有一个具有联合类型的值,我们只能访问成员 通用于所有类型的工会。

interface Bird { 
    fly(); 
    layEggs(); 
} 

interface Fish { 
    swim(); 
    layEggs(); 
} 

function getSmallPet(): Fish | Bird { 
    // ... 
} 

let pet = getSmallPet(); 
pet.layEggs(); // okay 
pet.swim(); // errors 

这里,getSmallPet返回类型既不是Fish也不Bird,但两者具有作为成员的成员共有的FishBird的超类型。 A Fish也是 a Fish | Bird,因此是Bird

与类型类会发生什么情况是完全不同的:

foo :: Num a => a -> a 
foo x = (x * x) + x 

虽然两者foo (3 :: Integer)foo (7.7 :: Double)工作,这并不意味着不存在对应于Num(3 :: Integer)(7.7 :: Double)也有一个超类型。相反,所有的Num a => a -> a说的是,你的a选择应具有的Num一个实例(这是值得强调的是,Num不是一个类型),所以有您所选择的类型(*)(+)合适的实现。与OOP方法不同,(*)(+)不属于任何特定类型,因此不需要引入超类型以便与IntegerDouble一起使用它们。

+0

这个答案有很大帮助,谢谢。尽管如此,我还是觉得这两个概念至少是相关的,因为'Pet'示例也可以通过类型类来实现。作为一个问题表达为:“类型级别约束与未标记的工会一样强大吗?” (尽管这可能更适合作为后续问题)。 – ThreeFx

+3

@ThreeFx我们可以说,在这种情况下,他们将是实现相同目标的两种截然不同的方式。顺便说一下,值得注意的是,未标记的方面对于讨论来说不是必需的 - 如果我们在TypeScript示例中有一个''Oviparous''接口和一个''''''''''''''''''可以扩展的'layEggs'方法,根本区别将保持不变。一个相关的,如果有些切向的问题:[*参数多态性与Ad-hoc多态性*](http://stackoverflow.com/q/6730126/2751851)。 – duplode