在SPJ的this论文中,在第3页和第4页,写着:
class Mutation m where
type Ref m :: * -> *
newRef :: a -> m (Ref m a)
readRef :: Ref m a -> m a
writeRef :: Ref m a -> a -> m ()
instance Mutation IO where
type Ref IO = IORef
newRef = newIORef
readRef = readIORef
writeRef = writeIORef
instance Mutation (ST s) where
type Ref (ST s) = STRef s
newRef = newSTRef
readRef = readSTRef
writeRef = writeSTRef
和
类声明现在引入了一个类型函数Ref(带有一个 指定类型)与通常的值函数(如newRef)一起使用 (每个都有指定的类型)。同样,每个实例声明 在实例类型中提供定义类型函数的子句 与每个价值功能的见证人一起。
我们说Ref是一种类型 家庭,或Mutation类的相关类型。它表现得像一个 类型级别的功能,所以我们也称Ref为类型函数。 应用类型函数使用与应用类型相同的语法 构造函数:参考上面的意思是将类型函数Ref应用于m, 然后将结果类型构造函数应用于。
所以,换句话说,
Ref :: (*->*)->*->*
即,Ref
将类型级别函数作为参数,如Maybe
或IO
或[]
,并生成另一个类型级别函数,例如{{1}使用模式匹配,即IORef
由模式匹配定义。
那么,怎么可能在类型级别而不是在值级别上对函数进行模式匹配?
例如,
Ref
无法写入,因为函数上的相等性为undecidable。
1)那么在类型级别上怎么可能没有问题呢?
2)是因为类型级别的函数排序非常有限吗?因此,类型级别上的任何类型的函数都不能是fun2int:: (Int->Int)->Int
fun2int (+)=2
fun2int (*)=3
的参数,只有少数几个,即程序员声明的函数而不是(+)函数,这些函数比通过声明的函数更通用。程序员?这就是为什么在类型级别功能模式匹配导致没问题的原因?
3)这个问题的答案是否与GHC规范的this部分有关?
答案 0 :(得分:7)
简而言之,类型级函数值上没有模式匹配,但仅限于名称。
在Haskell中,与许多其他语言一样,类型按名称分开,即使它们的表示相同。
data A x = A Int x
data B x = B Int x
上面,A
和B
是两种不同的类型构造函数,即使它们描述了相同的类型级函数:粗略地在伪代码\x -> (Int, x)
中。
从某种意义上说,这两个相同的类型级函数具有不同的名称/身份。
这与
不同type C x = (Int, x)
type D x = (Int, x)
它们都描述了与上面相同的类型级函数,但是没有引入两个新的类型名称。以上只是同义词:它们表示一个函数,但没有自己独特的身份。
这就是为什么可以添加A x
或B x
的类实例,而不是C x
或D x
的实例:尝试执行后者会添加一个实例类型为(Int, x)
,而是将实例与类型名称(,)
,Int
相关联。
在价值水平上,情况并没有那么大的不同。实际上,我们有值构造函数,它们是具有名称/标识的特殊函数,以及没有实际标识的常规函数。我们可以根据构造函数构建的模式进行模式匹配,但不能与其他任何内容进行匹配
case expOfTypeA of A n t -> ... -- ok
case someFunction of succ -> ... -- no
请注意,在类型级别,我们无法轻松进行模式匹配。 Haskell只允许使用类型类。这样做是为了保留一些理论属性(参数),并允许有效的实现(允许类型擦除 - 我们不必在运行时用其类型标记每个值)。这些功能的代价是限制类型级别模式匹配类型类 - 这确实给程序员带来了一些负担,但好处大于缺点。
答案 1 :(得分:7)
{-# LANGUAGE TypeFamilies, DataKinds, PolyKinds #-}
import GHC.TypeLits
让我们在Haskell中的类型和值级别之间绘制一些相似之处。
首先,我们在类型和价值级别都有无限制的功能。在类型级别,您可以使用类型系列表达几乎任何内容。 无法模式匹配任何类型或值级别的任意函数。例如。你不能说
type family F (a :: *) :: *
type family IsF (f :: * -> *) :: Bool where
IsF F = True
IsF notF = False
-- Illegal type synonym family application in instance: F
-- In the equations for closed type family ‘IsF’
-- In the type family declaration for ‘IsF’
第二,我们已完全应用数据和类型构造函数,例如值级别上的Just 5 :: Maybe Integer
或类型级别上的Just 5 :: Maybe Nat
。
可以对这些进行模式匹配:
isJust5 :: Maybe Integer -> Bool
isJust5 (Just 5) = True
isJust5 _ = False
type family IsJust5 (x :: Maybe Nat) :: Bool where
IsJust5 (Just 5) = True
IsJust5 x = False
注意任意函数和类型/数据构造函数之间的区别。构造函数的属性有时被称为 generativity 。对于两个不同的函数f
和g
,某些f x = g x
的{{1}}可能很有可能。另一方面,对于构造函数,x
暗示f x = g x
。这种差异使得第一种情况(任意函数上的模式匹配)不可判定,第二种情况(完全应用的构造函数上的模式匹配)可判定且易于处理。
到目前为止,我们已经看到了类型和价值水平的一致性。
最后,我们已部分应用(包括未应用)构造函数。在类型级别,这些包括f = g
,Maybe
和IO
(未应用),以及[]
和Either String
(部分应用)。在价值级别,我们未应用(,) Int
和Just
,部分应用了Left
和(,) 5
。
生成条件并不关心申请是否已满;所以没有什么能阻止部分应用的构造函数的模式匹配。这就是你在类型层面看到的;我们也可以在价值水平上拥有它。
(:) True
不支持这一点的原因是效率。类型级别的计算 在编译时以解释模式执行;所以我们可以负担得起 携带大量关于类型和类型函数的元数据以进行模式匹配 它们。
编译价值级别的计算,并且需要尽可能快 可能。保持足够的元数据以部分支持模式匹配 应用的构造函数会使编译器复杂化并减慢程序的速度 运行;为这样一个异国情调的功能付出太多了。