Section: 5 Type Classes

第 5 节 类型类

Names in the basic class structure of Haskell cannot be hidden (they are in PreludeCore) so we have to modify the names used in the tutorial.

Haskell 基本类结构中的名字无法被隐藏(它们在 PreludeCore 中),

所以我们不得不修改教程中使用的名字。

Here is a new Eq class:

这是一个新的 Eq 类:

class Eq' a where
  eq :: a -> a -> Bool

Now we can define elem using eq from above:

现在我们可以用上面的 eq 来定义 elem:

elem :: (Eq' a) => a -> [a] -> Bool
x `elem` [] = False
x `elem` (y:ys) = x `eq` y || x `elem` ys

Before this is of any use, we need to admit some types to Eq'

在这发挥作用之前,我们需要先把一些类型纳入 Eq'。

instance Eq' Int where
 x `eq` y = abs (x-y) < 3  -- Let's make this `nearly equal' just for fun
instance Eq' Float where
 x `eq` y = abs (x-y) < 0.1
list1 :: [Int]
list1 = [1,5,9,23]
list2 :: [Float]
list2 = [0.2,5.6,33,12.34]
e1 = 2 `elem` list1
e2 = 100 `elem` list1
e3 = 0.22 `elem` list2

Watch out! Integers in Haskell are overloaded - without a type signature to designate an integer as an Int, expressions like 3 eq 3 will be ambiguous.

小心!Haskell 中的整数是重载的——如果没有类型签名指明某个整数是

Int,像 3 eq 3 这样的表达式就会有歧义。

Now to add the tree type:

现在加入树类型:

data Tree a = Leaf a | Branch (Tree a) (Tree a)   deriving Show
instance (Eq' a) => Eq' (Tree a) where
  (Leaf a)       `eq` (Leaf b)       = a `eq` b
  (Branch l1 r1) `eq` (Branch l2 r2) =  (l1 `eq` l2) && (r1 `eq` r2)
  _              `eq` _              = False
tree1,tree2 :: Tree Int
tree1 = Branch (Leaf 1) (Leaf 2)
tree2 = Branch (Leaf 2) (Leaf 1)
e4 = tree1 `eq` tree2

Now make a new class with Eq' as a super class:

现在定义一个新类,以 Eq' 作为它的超类:

class (Eq' a) => Ord' a where
 lt,le :: a -> a -> Bool          -- lt and le are operators in Ord'
 x `le` y = x `eq` y || x `lt` y  -- This is a default for le

The typing of lt & le is

lt 和 le 的类型是

 le,lt :: (Ord' a) => a -> a -> Bool

This is identical to

这与下面这个完全相同:

 le,lt :: (Eq' a,Ord' a) => a -> a -> Bool

Make Int an instance of Ord':

让 Int 成为 Ord' 的一个实例:

instance Ord' Int where
 x `lt` y = x < y+1
i :: Int  -- Avoid ambiguity
i = 3
e5 :: Bool
e5 = i `lt` i

Some constraints on instance declarations:

A program can never have more than one instance declaration for a given combination of data type and class. If a type is declared to be a member of a class, it must also be declared in all superclasses of that class. An instance declaration does not need to supply a method for every operator in the class. When a method is not supplied in an instance declaration and no default is present in the class declaration, a runtime error occurs if the method is invoked. You must supply the correct context for an instance declaration -- this context is not inferred automatically.

实例声明的一些约束:

一个程序中,对同一"数据类型 + 类"的组合,绝不能有多个实例声明。

如果一个类型被声明为某个类的成员,它也必须被声明为该类的每一个

超类的成员。

实例声明不必为类中的每个运算符都提供方法。如果实例声明中没有提供

某个方法,而类声明中又没有默认实现,那么一旦调用该方法就会发生

运行时错误。

你必须为实例声明提供正确的上下文(context)——这个上下文不会被

自动推断。

This definition of Functor is in the Prelude:

Functor 的这个定义在 Prelude 中:

class Functor f where

fmap :: (a -> b) -> f a -> f b -- a generalized map function

This makes Tree an instance of Functor.

这让 Tree 成为 Functor 的一个实例。

instance Functor Tree where
   fmap f (Leaf x)       = Leaf   (f x)
   fmap f (Branch t1 t2) = Branch (fmap f t1) (fmap f t2)
e6 = fmap (+1) tree1

We can also make 2-tuples an instance of Functor:

我们也可以让 2 元组成为 Functor 的实例:

instance Functor ((,) a) where
  fmap f (x,y) = (x,f y)
e7 = fmap (+1) (1,2)

Note that these are a kind errors:

注意,下面这两个是种类(kind)错误:

-- instance Functor (,)
-- instance Functor Bool

The error message from Hugs isn't very useful but if you use :set +k then you will see a better message.

Hugs 给出的错误信息不太有用,但如果使用 :set +k,你会看到更好的

提示信息。