为什么参数化类型实例无需指定类型参数即可工作

时间:2019-05-15 07:01:25

标签: haskell typeclass type-parameter

具有参数化类型时: data A a=X a| Y

我尝试(成功地)实现FunctorApplicative而不指定类型参数:

instance Functor A where而不是instance Functor (A a) where
为什么起作用?

LYAH,似乎所有示例都在其所有typeclass instances中指定了类型参数。 什么时候应该忽略类型参数?

2 个答案:

答案 0 :(得分:11)

  

instance Functor A where而不是instance Functor (A a) where。   为什么起作用?

我发现使用GHC的分类系统更容易理解。让我们从一个简单的案例开始,并在GHCi中进行实验:

> :k Eq Int
Eq Int :: Constraint

这告诉我们Eq Int是一个约束,可以在类型检查期间验证某些属性。实际上,如果我们输入check (12 :: Int) == (42 :: Int),编译器将验证是否可以比较整数,从而解决约束Eq Int

Eq是什么,没有Int参数的类的名称?

> :k Eq
Eq :: * -> Constraint

这告诉我们Eq可以看作是从类型(*是类型的类型)到约束类型的函数。

实际上,在Eq Int中,Int是一种类型,因此我们有Int :: *使Int成为传递给Eq的良好参数。 / p>

类型类足够多,类型构造函数呢?

> :k Maybe Int
Maybe Int :: *

毫不奇怪,Maybe Int是一种类型

> :k Maybe
Maybe :: * -> *

Maybe是一个从类型到类型(*->*)的函数。实际上,Maybe类型构造函数所做的就是:将类型(Int)映射到类型(Maybe Int)。

回到原始问题。为什么我们不能写instance Functor (A a),却可以写instance Functor A?好吧,我们有

> :k A Int
A Int :: *
> :k A
A :: * -> *

最重要的是

> :k Functor
Functor :: (* -> *) -> Constraint

这告诉我们Functor类型类的类型与Eq类型类的类型不同。 Eq期望类型作为参数,而Functor期望类型(* -> *)作为参数。 A适合这种类型,而A Int不适合这种类型。

当在类的定义中将参数应用于其他某种类型时,就会发生这种情况。例如

class C1 a where
   foo :: a -> Bool

产生C1 :: * -> Constraint。相反,

class C2 f where
   bar :: f Int -> Bool

产生C2 :: (* -> *) -> Constraint,因为f本身不被用作类型,f Int被使用,所以f必须是类型* -> *的参数化类型

答案 1 :(得分:7)

  

什么时候应该忽略type参数?

类型参数不是“ 被忽略”。让我们首先看一下Functor类型类:

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

请注意类型签名中的f af b。因此,我们在这里为fmap的类型签名“构造类型”。

因此f基本上是一个接受类型并将其转换为类型的函数。例如,如果f ~ Aa ~ Int,则f a生成A Int类型。

Learn You a Haskell for the Greater Good!实际上在其有关 Functor,Applicatives和Monoids 的章节中对此进行了解释:

  

很多时候,我们想让我们的类型实例成为某种类型   类,但类型参数与我们想要的不匹配   去做。 Maybe设为Functor 的实例很容易,因为   Functor类型类的定义如下:

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

所以我们刚开始:

  instance Functor Maybe where
     

然后实施fmap。所有类型参数的总和是因为Maybe在   f类型类的定义,因此,如果我们看一下Functor   就像它仅适用于fmap一样,最终表现为:

Maybe
     

(...)