Haskell 在类型级别的函数上进行模式匹配是可能的,但在值级别上不可能,为什么会有这种差异?
SPJ在第3页和第4页的论文中写道:Haskell 在类型级别的函数上进行模式匹配是可能的,但在值级别上不可能,为什么会有这种差异?,haskell,type-families,higher-order-types,Haskell,Type Families,Higher Order Types,SPJ在第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 readRe
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是一种类型
家族,或类突变的相关类型。它的行为就像一个
函数位于类型级别,因此我们也将Ref称为类型函数。
应用类型函数使用与应用类型相同的语法
构造函数:上面的Ref m a表示将类型函数Ref应用于m,
然后将生成的类型构造函数应用于
那么换句话说,
Ref::(*->*)->*->*
也就是说,Ref
将类型级函数作为参数,例如可能
或IO
或[]
,并使用模式匹配生成另一个类型级函数,例如IORef
,即Ref
由模式匹配定义
那么,如何可能在类型级别的函数上进行模式匹配,而在值级别上不进行模式匹配呢
比如说,
fun2int:: (Int->Int)->Int
fun2int (+)=2
fun2int (*)=3
无法写入,因为函数上的相等是不可能的
1) 那么,在类型级别上,这怎么可能没有问题呢
2) 是不是因为类型级别上的函数种类非常有限?因此,类型级别上没有任何类型的函数可以成为Ref
的参数,只有少数几个,即程序员声明的函数,而不是像(+)这样的函数,它们比程序员声明的函数更通用?这就是为什么在类型级别上函数模式匹配不会导致问题的原因吗
3) 这个问题的答案是否与GHC规范的一部分有关?简单地说,类型级别的函数值没有模式匹配,只有名称匹配 与许多其他语言一样,在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)
它们都描述了与上面相同的类型级别函数,但没有引入两个新的类型名。以上只是同义词:它们表示一个函数,但没有自己独特的标识
这就是为什么可以为ax
或bx
添加类实例,但不能为cx
或dx
添加类实例的原因:尝试添加后一个实例会将实例添加到类型(Int,x)
,而将实例与类型名(,)
,Int
关联
在价值层面上,情况并没有太大不同。实际上,这里有值构造函数,它们是具有名称/标识的特殊函数,以及没有实际标识的正则函数。我们可以根据一个由构造函数构建的模式进行模式匹配,但不能与任何其他模式进行匹配
case expOfTypeA of A n t -> ... -- ok
case someFunction of succ -> ... -- no
请注意,在类型级别,我们无法轻松地进行模式匹配。Haskell只允许利用类型类来实现这一点。这样做是为了保留一些理论属性(参数性),并允许有效的实现(允许类型擦除——我们不必在运行时用其类型标记每个值)。这些特性的代价是限制类型级模式匹配到类型类——这确实给程序员带来了一些负担,但好处大于缺点
{-# 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’
Second,我们已经完全应用了数据和类型构造函数,例如值级别上的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
请注意任意函数和类型/数据构造函数之间的区别。构造函数的属性有时称为生成性。对于两种不同的函数f
和g
,对于某些x
,很可能fx=gx
。另一方面,对于构造函数,fx=gx
意味着f=g
。这种差异使得第一种情况(任意函数上的模式匹配)不可判定,而第二种情况(完全应用的构造函数上的模式匹配)可判定且易于处理
到目前为止,我们已经看到了类型和值级别的一致性
最后,我们已经部分应用了(包括未应用的)构造函数。在类型级别上,包括可能
、IO
和[]
(未应用)以及字符串
和(,)Int
(部分应用)。在值级别上,我们只应用了和左,部分应用了(,)5
和(:)True
生成性条件不关心应用程序是否已满;因此,对于部分应用的构造函数,没有什么可以排除模式匹配。这就是你在类型层面上看到的;我们也可以在价值层面上拥有它
type family IsLeft (x :: k -> k1) :: Bool where
IsLeft Left = True
IsLeft x = False
isLeft :: (a -> Either a b) -> Bool
isLeft Left = True
isLeft _ = False
-- Constructor ‘Left’ should have 1 argument, but has been given none
-- In the pattern: Left
-- In an equation for ‘isLeft’: isLeft Left = True
不支持这一点的原因是效率。类型级的计算
在编译时以解释模式执行;所以我们