为什么我不能使用这种非参数化类型创建实例?

时间:2012-10-25 21:05:40

标签: haskell typeclass

为什么这段代码正确

instance Functor IO where -- note that IO isn't parametrized, and it's correct
    fmap f action = do
        result <- action
        return (f result)

但以下代码有编译错误?

class Print a where
    print :: a -> String

data A t = A t
instance Print A where  -- error: expecting one more argument to `A'
    print a = "abc"

3 个答案:

答案 0 :(得分:11)

这是因为kinds不匹配。常规类型具有类型*,而类型构造函数(例如AIO)具有类* -> *,表示它们需要类型参数才能返回类型。

Print类的定义中,编译器推断由于a用作普通类型,因此它必须具有类*。但是,Functor适用于类* -> *的类型构造函数:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

这里,f不是用作普通类型,而是用作类型构造函数,因此推断类型为* -> *。您可以使用GHCi中的:kind命令验证这一点:

> :kind Print
Print :: * -> Constraint
> :kind Functor
Functor :: (* -> *) -> Constraint 

答案 1 :(得分:9)

当你说

class Print a where
   print' :: a -> String

您确保a必须是一种类型,但是当您说

data A t = A t

您使A成为类型构造函数 - A不是类型,但例如A IntA是一种关于类型的函数,但Print类中的a必须是类型值,而不是类型函数。

你可以做到

instance Print (A Int) where
  print' a = "abc"

IO没关系,因为Functor类要求输入类型构造函数。

class Functor f where
  fmap :: (a -> b) -> f a -> f b

您可以看到,由于f a是一种类型,f是一种类型构造函数,就像IOA一样。 你将能够做到

instance Functor A where  -- OK, A is a constructor, Functor needs one
  fmap f (A x) = A (f x)

你将无法做到

instance Eq IO where -- not OK - IO is a constructor and Eq needs a type
    (==) = error "this code won't compile"

(我使用了print'代替print来避免与标准函数print发生冲突。)

答案 2 :(得分:1)

在心理上(或使用文本编辑器)尝试使用您在实例中使用的类型填写类定义中给出的类型。

自:

class Print a where
    print :: a -> String

data A t = A t

我们想要

instance Print A

所以,在类型类定义中用a替换A我们说的是一个实例,我们得到了这个:

class Print A where
    print :: A -> String

糟糕, A -> String作为一种类型没有意义,因为函数类型箭头左侧是一个类型,右侧是一个类型,并为您提供函数类型。但A不是类型,因为您使用A声明了data A t; A t是任何类型t的类型,但A类型构造函数。如果将它应用于类型,它可以创建一个类型,但A本身是不同的。因此,您可以A t成为Print的实例,而不是A本身。

为什么instance Functor IO有效?让我们看一下类定义:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

现在让我们尝试用IO代替f

class Functor IO where
    fmap :: (a -> b) -> IO a -> IO b

IO s最终应用于类型参数,因此一切正常。如果我们尝试将IntA t这样的具体类型设为Functor的实例,我们就会遇到问题。

相关问题