为什么参数化类型实例在不指定类型参数的情况下工作
Why does parametrized type instance works without specifying type parameter
当具有参数化类型时:
data A a=X a| Y
我已经尝试(成功)实现了 Functor
和 Applicative
而没有指定类型参数:
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 a
和 f 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]的参数化类型=].
当具有参数化类型时:
data A a=X a| Y
我已经尝试(成功)实现了 Functor
和 Applicative
而没有指定类型参数:
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 a
和 f 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 ofFunctor
, because theFunctor
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 theMaybe
takes the place off
in the definition of theFunctor
type class and so if we look atfmap
like it only worked onMaybe
, it ends up behaving like:fmap :: (a -> b) -> Maybe a -> Maybe b
(...)
instance Functor A where
instead ofinstance 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]的参数化类型=].