Haskell使用数据类型

时间:2014-05-13 02:43:16

标签: haskell

我正在阅读making our own types and typeclasses以了解你的问题。

Algebraic data types intro部分中,我注意到:

data Point = Point Float Float deriving (Show)  
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)

surface :: Shape -> Float
surface (Rectangle (Point x1 y1) (Point x2 y2)) = abs (x2 - x1) * abs (y2 - y1) 

surface (Rectangle (Point x1 y1) (Point x2 y2))中,我们指出Rectangle的参数类型为Point。

但是,在Recursive data structures部分中:

data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
singleton :: a -> Tree a  
singleton x = Node x EmptyTree EmptyTree  

treeInsert :: (Ord a) => a -> Tree a -> Tree a  
treeInsert x EmptyTree = singleton x  
treeInsert x (Node a left right)   
    | x == a = Node x left right  
    | x < a  = Node a (treeInsert x left) right  
    | x > a  = Node a left (treeInsert x right)

我们不会在left中指出rightTree a的数据类型为treeInsert x (Node a left right)。编译器如何知道它们的类型?

2 个答案:

答案 0 :(得分:13)

我认为你有一个误解:

  

surface (Rectangle (Point x1 y1) (Point x2 y2))中,我们指出Rectangle的参数类型为Point

这确实表明参数属于point类型,但可能与您的想法不同。 Point中的“Point x1 y1”不是一个类型 - 它是一个构造函数,其命名方式与它构造的类型相同。如果我们将Point声明为

data Point = MakePoint Float Float

然后你会说

surface (Rectangle (MakePoint x1 y1) (MakePoint x2 y2)) 

为清楚起见,我将继续使用MakePoint作为构造函数,Point作为类型。合法的Haskell将其命名为相同,因为编译器总是可以从上下文中判断,但人类有时会遇到更多麻烦。

在上下文中

surface (Rectangle (MakePoint x1 y1) (MakePoint x2 y2)) = ...

我们知道子表达式MakePoint x1 y1具有来自两个不同位置的类型Point。一个是构造函数Rectangle具有类型

Rectangle :: Point -> Point -> Shape

所以我们知道它的两个参数都必须是点(这是 outside-in 类型推断,我们从使用它的上下文中获取某种类型的东西);另一个是构造函数MakePoint具有类型

MakePoint :: Float -> Float -> Point

所以我们知道MakePoint x1 y1代表Point类型的值(这是 inside-out 类型推断,我们从其组件中获取表达式的类型) 。在某种程度上,编译器使用这两种方法并确保它们匹配。

但是,有时缺少这些信息中的一种或另一种,例如我们的例子中的x1。我们没有关于x1的内外信息(好吧,如果我们查看方程式的右侧,编译器也会这样做,但我们现在忽略它),我们所拥有的只是MakePoint构造函数的参数必须为Float s,因此我们知道x1必须是Float。这是编译器有效和推断的;没有必要明确说明。

Tree示例中,有更多令人困惑的命名(一旦你得到它,不再混淆并开始有用,但最好在开始时画出明显的区别),所以我我要将Node的第一个参数重命名为av

treeInsert :: (Ord a) => a -> Tree a -> Tree a  
treeInsert x EmptyTree = singleton x  
treeInsert x (Node v left right)   
    | x == v = Node x left right  
    | x < v  = Node v (treeInsert x left) right  
    | x > v  = Node v left (treeInsert x right)

leftright与上面的x1一样发生了同样的事情:没有内部结构可供使用,但我们知道Node构造函数需要a和两个Tree a,因此v 必须属于a类型,left和{{1} 必须属于right类型。编译器从上下文中推断出这一点。

答案 1 :(得分:2)

  

在表面(Rectangle (Point x1 y1) (Point x2 y2))中,我们指出Rectangle的参数是Point类型。

没有。这是你的误解。由于数据声明,编译器知道Rectangle参数的类型:

data ... | Rectangle Point Point

在您引用的代码中:

surface (Rectangle (Point x1 y1) (Point x2 y2))

这称为模式匹配。 Surface采用矩形,我们将模式匹配,以便将变量名称绑定到参数。我们还对每个参数进行模式匹配,以获取对子参数的访问权限,并绑定变量名称x1y1x2y2