具有参数化类型时:
data A a=X a| Y
我尝试(成功地)实现Functor
和Applicative
而不指定类型参数:
instance Functor A where
而不是instance Functor (A a) where
。
为什么起作用?
看LYAH,似乎所有示例都在其所有typeclass instances
中指定了类型参数。
什么时候应该忽略类型参数?
答案 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 a
和f b
。因此,我们在这里为fmap
的类型签名“构造类型”。
因此f
基本上是一个接受类型并将其转换为类型的函数。例如,如果f ~ A
和a ~ 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
(...)