在 Haskell 中,"higher-kinded types" *真的* 类型吗?或者它们只是表示*具体*类型的集合,仅此而已?
In Haskell, are "higher-kinded types" *really* types? Or do they merely denote collections of *concrete* types and nothing more?
参数化多态函数
考虑以下函数:
f :: a -> Int
f x = (1 :: Int)
我们可以说 f
的类型是 a -> Int
,因此 f
是 "polymorphic" 类型。
以下哪项是对 f
最准确的思考方式?
实际上有一个单f
类型a -> Int
。但是,它可以用作 f :: Int -> Int
、f :: Double -> Int
等。
从字面上看,f
的类型不是a -> Int
。实际上,这只是一种 shorthand 的说法,即有一个 族 的函数 f
其类型是具体的(即,有一个 f :: Int -> Int
、f :: Double -> Double
,等等;此外,这些功能中的每一个都彼此不同)。
高等类型
同样,我们可以考虑如下类型声明:
data Maybe a = Just a | Nothing
并问这两种观点哪个更正确:
没有单类型Maybe
;实际上,只有一系列具体类型(Maybe Int
、Maybe String
等),仅此而已。
实际上有一个类型Maybe
。这种类型是高等类型。当我们说它是 "type" 时,我们的意思是字面意思(而不是 (1) 的 shorthand)。碰巧我们也可以编写 Maybe Int
、Maybe Double
等来生成 distinct 类型(恰好是具体的)。但是,在一天结束时(即):Maybe
、Maybe Int
和 Maybe String
表示 三个 不同的类型,其中两个是具体的其中之一是更高等的。
问题总结
在Haskell中,"higher-kinded types"真的是类型吗?或者只是具体类型 "the real types",当我们说到 "higher-kinded types" 时,我们只是表示一个 家族 的具体类型。此外,参数多态函数是表示单一类型的函数,还是仅表示集合 具体类型的功能(仅此而已)?
首先问一个问题:语句"all lists have length"是单个语句还是一系列语句"list1 has length"、"list2 has length"、...?
如果你用明确的 forall
给出 f
的类型,你会得到 f :: forall a. a -> Int
。首先,这不是"higher-kinded"。我们可以在 GHCI 中进行以下操作:
λ> :set -XRankNTypes
λ> :k (forall a. a -> Int)
(forall a. a -> Int) :: *
所以f
有一种*
。
现在,在 Haskell 中,我们可以使用 ~
来实现类型相等。我们可以设置以下内容来检查 GHCI 中的内容:
λ> :set -XImpredicativeTypes
λ> :set -XTypeFamilies
λ> :t undefined :: ((~) Int Int) => a
undefined :: ((~) Int Int) => a :: a
这表明 GHC 为这个例子找出了类型相等性。类型不等会报如下错误:
λ> undefined :: ((~) (Int -> Int) (Int)) => a
<interactive>:22:1:
Couldn't match expected type ‘Int’ with actual type ‘Int -> Int’
In the expression: undefined :: ((~) (Int -> Int) (Int)) => a
In an equation for ‘it’:
it = undefined :: ((~) (Int -> Int) (Int)) => a
现在直接使用此方法将阻止我们比较 f
的类型,但我发现一个轻微的变体应该适用于我们的目的:
λ> :t undefined :: forall a. ((a -> Int) ~ (Int -> Int)) => a
undefined :: forall a. ((a -> Int) ~ (Int -> Int)) => a :: Int
换句话说,如果 f
类型等价于 g :: Int -> Int
,那么 a
必须是 Int。这类似于x = y
,y = 0
所以x = 0
。在我们指定 y = 0
之前,我们没有 x = 0
,在那之前我们只有 x = y
.
Maybe
是不同的,因为它有以下种类:
λ> :k Maybe
Maybe :: * -> *
因为我们使用的是 DataKinds
,所以我们有 :k (~) :: k -> k -> GHC.Prim.Constraint
,所以我们可以这样做:
λ> :t undefined :: (~) Maybe Maybe => Int
undefined :: (~) Maybe Maybe => Int :: Int
λ> :k Either ()
Either () :: * -> *
λ> :t undefined :: (~) Maybe (Either ()) => Int
Couldn't match expected type ‘Either ()’ with actual type ‘Maybe’
总而言之,f :: forall a. a -> Int
与语句 "if you give me anything, I'll give you an Int" 一样有意义。你能把这个语句翻译成一堆语句 "if you give me a dog..", "if you give me a penny.." 吗?是的,但它削弱了声明。最后,准确确定 "same" 的含义,您就会得到答案。
不完全清楚您想问什么,以及两种情况下 1 和 2 之间的实际区别是什么,但从基础数学的角度来看:
参数多态函数
f
实际上有类型 f :: forall a.a->int
它是 Haskell 所基于的类型化 lambda 演算中函数的完全合法类型。它可以是这样的:
f = λa:Type.λx:a.(body for f)
如何从中得到 Double->Int
?你应用它到Double
类型:
f Double = (λa:Type.λx:a.(body for f)) Double => λx:Double.(body for f|a=Double)
Haskell 在幕后执行两种操作(类型抽象和类型应用),尽管可以使用 XExplicitForAll
GHC 扩展在类型签名中明确声明 forall
部分,并显式创建 f
的 Double->Int
实例,类型签名为:
f_double :: Double -> Int
f_double = f
高等类型
考虑一个简单的类型:
data Example = IntVal Int | NoVal
(没错,就是Maybe Int
)。
Maybe
是一个 type 构造函数,就像 IntVal
是一个 data 构造函数一样。这是完全相同的事情,只是 'one level higher',在 Maybe
应用于 Type
的意义上,很像 IntVal
应用于 Int
。
在 lambda 演算中,Maybe
具有类型:
Maybe : Type->Type
Haskell 不允许您从类型构造函数中获取类型,但允许您获取 kind(这只是 类型):
:k Maybe
Maybe :: * -> *
所以不,Maybe
不是 类型 :您不能拥有类型为 Maybe
的对象。 Maybe
(几乎)是一个从类型到类型的函数,就像 IntVal
是一个从值到值的函数。
我们将Maybe
应用于String
的结果称为Maybe String
,就像我们将IntVal
应用于4
的结果称为IntVal 4
.
参数化多态函数
考虑以下函数:
f :: a -> Int
f x = (1 :: Int)
我们可以说 f
的类型是 a -> Int
,因此 f
是 "polymorphic" 类型。
以下哪项是对 f
最准确的思考方式?
实际上有一个单
f
类型a -> Int
。但是,它可以用作f :: Int -> Int
、f :: Double -> Int
等。从字面上看,
f
的类型不是a -> Int
。实际上,这只是一种 shorthand 的说法,即有一个 族 的函数f
其类型是具体的(即,有一个f :: Int -> Int
、f :: Double -> Double
,等等;此外,这些功能中的每一个都彼此不同)。
高等类型
同样,我们可以考虑如下类型声明:
data Maybe a = Just a | Nothing
并问这两种观点哪个更正确:
没有单类型
Maybe
;实际上,只有一系列具体类型(Maybe Int
、Maybe String
等),仅此而已。实际上有一个类型
Maybe
。这种类型是高等类型。当我们说它是 "type" 时,我们的意思是字面意思(而不是 (1) 的 shorthand)。碰巧我们也可以编写Maybe Int
、Maybe Double
等来生成 distinct 类型(恰好是具体的)。但是,在一天结束时(即):Maybe
、Maybe Int
和Maybe String
表示 三个 不同的类型,其中两个是具体的其中之一是更高等的。
问题总结
在Haskell中,"higher-kinded types"真的是类型吗?或者只是具体类型 "the real types",当我们说到 "higher-kinded types" 时,我们只是表示一个 家族 的具体类型。此外,参数多态函数是表示单一类型的函数,还是仅表示集合 具体类型的功能(仅此而已)?
首先问一个问题:语句"all lists have length"是单个语句还是一系列语句"list1 has length"、"list2 has length"、...?
如果你用明确的 forall
给出 f
的类型,你会得到 f :: forall a. a -> Int
。首先,这不是"higher-kinded"。我们可以在 GHCI 中进行以下操作:
λ> :set -XRankNTypes
λ> :k (forall a. a -> Int)
(forall a. a -> Int) :: *
所以f
有一种*
。
现在,在 Haskell 中,我们可以使用 ~
来实现类型相等。我们可以设置以下内容来检查 GHCI 中的内容:
λ> :set -XImpredicativeTypes
λ> :set -XTypeFamilies
λ> :t undefined :: ((~) Int Int) => a
undefined :: ((~) Int Int) => a :: a
这表明 GHC 为这个例子找出了类型相等性。类型不等会报如下错误:
λ> undefined :: ((~) (Int -> Int) (Int)) => a
<interactive>:22:1:
Couldn't match expected type ‘Int’ with actual type ‘Int -> Int’
In the expression: undefined :: ((~) (Int -> Int) (Int)) => a
In an equation for ‘it’:
it = undefined :: ((~) (Int -> Int) (Int)) => a
现在直接使用此方法将阻止我们比较 f
的类型,但我发现一个轻微的变体应该适用于我们的目的:
λ> :t undefined :: forall a. ((a -> Int) ~ (Int -> Int)) => a
undefined :: forall a. ((a -> Int) ~ (Int -> Int)) => a :: Int
换句话说,如果 f
类型等价于 g :: Int -> Int
,那么 a
必须是 Int。这类似于x = y
,y = 0
所以x = 0
。在我们指定 y = 0
之前,我们没有 x = 0
,在那之前我们只有 x = y
.
Maybe
是不同的,因为它有以下种类:
λ> :k Maybe
Maybe :: * -> *
因为我们使用的是 DataKinds
,所以我们有 :k (~) :: k -> k -> GHC.Prim.Constraint
,所以我们可以这样做:
λ> :t undefined :: (~) Maybe Maybe => Int
undefined :: (~) Maybe Maybe => Int :: Int
λ> :k Either ()
Either () :: * -> *
λ> :t undefined :: (~) Maybe (Either ()) => Int
Couldn't match expected type ‘Either ()’ with actual type ‘Maybe’
总而言之,f :: forall a. a -> Int
与语句 "if you give me anything, I'll give you an Int" 一样有意义。你能把这个语句翻译成一堆语句 "if you give me a dog..", "if you give me a penny.." 吗?是的,但它削弱了声明。最后,准确确定 "same" 的含义,您就会得到答案。
不完全清楚您想问什么,以及两种情况下 1 和 2 之间的实际区别是什么,但从基础数学的角度来看:
参数多态函数
f
实际上有类型 f :: forall a.a->int
它是 Haskell 所基于的类型化 lambda 演算中函数的完全合法类型。它可以是这样的:
f = λa:Type.λx:a.(body for f)
如何从中得到 Double->Int
?你应用它到Double
类型:
f Double = (λa:Type.λx:a.(body for f)) Double => λx:Double.(body for f|a=Double)
Haskell 在幕后执行两种操作(类型抽象和类型应用),尽管可以使用 XExplicitForAll
GHC 扩展在类型签名中明确声明 forall
部分,并显式创建 f
的 Double->Int
实例,类型签名为:
f_double :: Double -> Int
f_double = f
高等类型
考虑一个简单的类型:
data Example = IntVal Int | NoVal
(没错,就是Maybe Int
)。
Maybe
是一个 type 构造函数,就像 IntVal
是一个 data 构造函数一样。这是完全相同的事情,只是 'one level higher',在 Maybe
应用于 Type
的意义上,很像 IntVal
应用于 Int
。
在 lambda 演算中,Maybe
具有类型:
Maybe : Type->Type
Haskell 不允许您从类型构造函数中获取类型,但允许您获取 kind(这只是 类型):
:k Maybe
Maybe :: * -> *
所以不,Maybe
不是 类型 :您不能拥有类型为 Maybe
的对象。 Maybe
(几乎)是一个从类型到类型的函数,就像 IntVal
是一个从值到值的函数。
我们将Maybe
应用于String
的结果称为Maybe String
,就像我们将IntVal
应用于4
的结果称为IntVal 4
.