为什么参数化类型实例在不指定类型参数的情况下工作

Why does parametrized type instance works without specifying type parameter

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

我已经尝试(成功)实现了 FunctorApplicative 而没有指定类型参数:

instance Functor A where 而不是 instance Functor (A a) where.
为什么它有效?

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

When should you neglect the type parameter?

类型参数不是“忽略”。我们先来看看Functor类型class:

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

注意类型签名中的 f af b。因此我们这里 "construct types" 的类型签名是 fmap.

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

Learn You a Haskell for the Greater Good! 实际上在其有关 Functors、Applicatives 和 Monoids 的章节中解释了这一点:

Many times, we want to make our types instances of certain type classes, but the type parameters just don't match up for what we want to do. It's easy to make Maybe an instance of Functor, because the Functor type class is defined like this:

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

So we just start out with:

  instance Functor Maybe where

And then implement fmap. All the type parameters add up because the Maybe takes the place of f in the definition of the Functor type class and so if we look at fmap like it only worked on Maybe, it ends up behaving like:

fmap :: (a -> b) -> Maybe a -> Maybe b

(...)

instance Functor A where instead of instance Functor (A a) where. Why does it work ?

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

> :k Eq Int
Eq Int :: Constraint

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

单独的Eq是什么,没有Int参数的class的名字?

> :k Eq
Eq :: * -> Constraint

这告诉我们Eq可以被认为是一个从类型(*是类型的种类)到约束的函数。

确实,在Eq Int中,Int是一个类型,所以我们Int :: *使Int成为一个well-kinded参数传递给Eq.

足够的类型 classes,类型构造函数呢?

> :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 类型 class 的种类与 Eq 类型的 class 不同。 Eq 期望类型作为参数,而 Functor 期望类型 (* -> *) 作为参数。 A 适合那种,而 A Int 不适合。

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

class C1 a where
   foo :: a -> Bool

结果 C1 :: * -> Constraint。相反,

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

结果为C2 :: (* -> *) -> Constraint,由于f本身不作为类型使用,f Int是,所以f必须是类型[=52]的参数化类型=].