本文为学习类型系统的系列文章的第一篇。主要参考资料为大名鼎鼎的 T y p e s Types Types a n d and and P r o g r a m m i n g Programming Programming L a n g u a g e s Languages Languages。
相信有很多跟我一样的读者都是从 C C C系语言如 J a v a Java Java, C / C P P C/CPP C/CPP等入门开始学习编程。而对我来讲,对于类型系统的概念仅仅停留在 J a v a Java Java的泛型语言之中。由于 J a v a Java Java的泛型相对来说较为简单,一度让我以为类型不过是些细枝末节,直到我接触到了 S c a l a Scala Scala, H a s k e l l Haskell Haskell这样的语言,才一点一点对类型系统有了较为清楚的认识,同时也开始学习相关方面的知识。
类型系统简介
首先来看看对于类型系统的定义
A A A t y p e type type s y s t e m system system i s is is a a a t r a c t a b l e tractable tractable s y n t a c t i c syntactic syntactic m e t h o d method method f o r for for p r o v i n g proving proving t h e the the a b s e n c e absence absence o f of of
c e r t a i n certain certain p r o g r a m program program b e h a v i o r s behaviors behaviors b y by by c l a s s i f y i n g classifying classifying p h r a s e s phrases phrases a c c o r d i n g according according t o to to t h e the the k i n d s kinds kinds
o f of of v a l u e s values values t h e y they they c o m p u t e . compute. compute.
类型系统有以下几个优点
- Detecting Errors(错误检查)
- Abstraction(抽象)
- Documentation(文档化)
- Language Safety(语言安全性)
- Efficiency(有效性)
- Further Applications(更多应用)
数学基础
需要读者有集合,序列,归纳等方面的知识,具体可自行查阅
无类型算术表达式
无类型算术表达式主要为了介绍诸如抽象语法,归纳定义和证明,求值,运行时错误等概念的建模。
首先我们需要定义算术表达式的语言形式。以BNF文法形式表达如下
t ::= terms:
true constant true
false constant false
if t then t else t conditional
0 constant zero
succ t successor
pred t predecessor
iszero t zero test
语法
归纳定义
- t r u e , f a l s e , 0 ⊆ T ; {true, false, 0} ⊆ T ; true,false,0⊆T;
- i f t 1 ∈ T , t h e n s u c c t 1 , p r e d t 1 , i s z e r o t 1 ⊆ T ; if t1 ∈ T ,then{succ t1, predt1,iszerot1}⊆T; ift1∈T,thensucct1,predt1,iszerot1⊆T;
- i f t 1 ∈ T , t 2 ∈ T , a n d t 3 ∈ T , if t1 ∈ T , t2 ∈ T , and t3 ∈ T , ift1∈T,t2∈T,andt3∈T, t h e n then then i f if if t 1 t1 t1 t h e n then then t 2 t2 t2 e l s e else else t 3 ∈ T . t3 ∈ T . t3∈T.
推导规则定义
- t r u e ∈ T false ∈ T 0 ∈ T true ∈ \text{T false} ∈ \text{T 0} ∈ T true∈T false∈T 0∈T
- t 1 ∈ T pred t1 ∈ T \frac{t1 ∈ T} {\text{pred t1} ∈ T} pred t1∈Tt1∈T t 1 ∈ T iszero t1 ∈ T \frac{t1 ∈ T} {\text{iszero t1} ∈ T} iszero t1∈Tt1∈T t 1 ∈ T succ t1 ∈ T \frac{t1 ∈ T} {\text{succ t1} ∈ T} succ t1∈Tt1∈T
- t1 ∈ T iszero t1 ∈ T t1 ∈ T t2 ∈ T t3 ∈ T if t1 then t2 else t3 ∈ T \frac{\text{t1 ∈ T iszero t1 ∈ T t1 ∈ T t2 ∈ T t3 ∈ T}}{\text{if t1 then t2 else t3 ∈ }T} if t1 then t2 else t3 ∈ Tt1 ∈ T iszero t1 ∈ T t1 ∈ T t2 ∈ T t3 ∈ T
具体定义
S 0 = ∅ S_0 = \emptyset S0=∅
S i + 1 = { true,false,0 } S_{i+1} =\quad \left\{\text{true,false,0}\right\} Si+1={true,false,0}
∪ { succ t1,pred t1,iszero t1 ∣ t 1 ∈ S i } \cup\left\{\text{succ t1,pred t1,iszero t1 }\mid t1 \in S_i\right\} ∪{succ t1,pred t1,iszero t1 ∣t1∈Si}
∪ { if t1,then t2 else t3 ∣ t 1 , t 2 , t 3 ∈ S i } \cup\left\{\text{if t1,then t2 else t3 }\mid t1,t2,t3 \in S_i\right\} ∪{if t1,then t2 else t3 ∣t1,t2,t3∈Si}
对项的归纳
常量集合
Consts(true)   = t r u e \text{Consts(true)} \qquad\qquad\qquad\ \,= {true} Consts(true) =true
Consts(false) = false \text{Consts(false) \qquad\qquad\qquad\ = {false}} Consts(false) = false
Consts(0)   = 0 \text{Consts(0) \qquad\qquad\qquad\quad\ \ \,= {0}} Consts(0) = 0
Consts(succ t1)   = Consts(t1) \text{Consts(succ t1) \qquad\qquad\quad\,= Consts(t1)} Consts(succ t1) = Consts(t1)
Consts(pred t1) = Consts(t1) \text{Consts(pred t1) \qquad\qquad\ \ \ \ = Consts(t1)} Consts(pred t1) = Consts(t1)
Consts(iszero t1) = Consts(t1) \text{Consts(iszero t1) \qquad\qquad\ \ = Consts(t1)} Consts(iszero t1) = Consts(t1)
Consts(if t1 then t2 else t3)   = Consts(t1) ∪ Consts(t2) ∪ Consts(t3) \text{Consts(if t1 then t2 else t3) \,= Consts(t1) ∪ Consts(t2) ∪ Consts(t3)} Consts(if t1 then t2 else t3) = Consts(t1) ∪ Consts(t2) ∪ Consts(t3)
长度
size(true)   = 1 \text{size(true)} \qquad\qquad\qquad\ \,= 1 size(true) =1
size(false) = 1 \text{size(false) \qquad\qquad\qquad\ = 1} size(false) = 1
size(0)   = 1 \text{size(0) \qquad\qquad\qquad\quad\ \ \,= 1} size(0) = 1
size(succ t1)   = size(t1) + 1 \text{size(succ t1) \qquad\qquad\quad\,= size(t1) + 1} size(succ t1) = size(t1) + 1
size(pred t1) = size(t1) + 1 \text{size(pred t1) \qquad\qquad\ \ \ \ = size(t1) + 1} size(pred t1) = size(t1) + 1
size(iszero t1) = size(t1) + 1 \text{size(iszero t1) \qquad\qquad\ \ = size(t1) + 1} size(iszero t1) = size(t1) + 1
size(if t1 then t2 else t3)   = size(t1) + size(t2) + size(t3) + 1 \text{size(if t1 then t2 else t3) \,= size(t1) + size(t2) + size(t3) + 1} size(if t1 then t2 else t3) = size(t1) + size(t2) + size(t3) + 1
深度
depth(true)   = 1 \text{depth(true)} \qquad\qquad\qquad\ \,= 1 depth(true) =1
depth(false) = 1 \text{depth(false) \qquad\qquad\qquad\ = 1} depth(false) = 1
depth(0)   = 1 \text{depth(0) \qquad\qquad\qquad\quad\ \ \,= 1} depth(0) = 1
depth(succ t1)   = depth(t1) + 1 \text{depth(succ t1) \qquad\qquad\quad\,= depth(t1) + 1} depth(succ t1) = depth(t1) + 1
depth(pred t1) = depth(t1) + 1 \text{depth(pred t1) \qquad\qquad\ \ \ \ = depth(t1) + 1} depth(pred t1) = depth(t1) + 1
depth(iszero t1) = depth(t1) + 1 \text{depth(iszero t1) \qquad\qquad\ \ = depth(t1) + 1} depth(iszero t1) = depth(t1) + 1
depth(if t1 then t2 else t3)   = max(depth(t1) , depth(t2) , depth(t3)) + 1 \text{depth(if t1 then t2 else t3) \,= max(depth(t1) , depth(t2) , depth(t3)) + 1} depth(if t1 then t2 else t3) = max(depth(t1) , depth(t2) , depth(t3)) + 1
引理
一 个 项 上 t 中 不 同 常 量 的 个 数 不 大 于 t 的 长 度 s i z e ( t ) , 即 ∣ C o n s t s ( t ) ∣ ≤ s i z e ( t ) 一个项上t中不同常量的个数不大于t的长度size(t),即|Consts(t)| \leq size(t) 一个项上t中不同常量的个数不大于t的长度size(t),即∣Consts(t)∣≤size(t)
定理(项上归纳原理)
假 设 P 是 项 上 的 一 个 谓 词 : 假设P是项上的一个谓词: 假设P是项上的一个谓词:
对 深 度 的 归 纳 : 如 果 对 每 个 项 s 对深度的归纳:如果对每个项s 对深度的归纳:如果对每个项s
假 设 对 所 有 使 得 d e p t h ( r ) < d e p t h ( s ) 的 项 r 有 P ( r ) , 我 们 能 证 明 P ( s ) 假设对所有使得depth(r)<depth(s)的项r有P(r),我们能证明P(s) 假设对所有使得depth(r)<depth(s)的项r有P(r),我们能证明P(s)
则 P ( s ) 对 所 有 的 s 成 立 则P(s)对所有的s成立 则P(s)对所有的s成立
对 长 度 的 归 纳 : 如 果 对 每 个 项 s 对长度的归纳:如果对每个项s 对长度的归纳:如果对每个项s
假 设 对 所 有 使 得 s i z e ( r ) < s i z e ( s ) 的 项 r 有 P ( r ) , 我 们 能 证 明 P ( s ) 假设对所有使得size(r)<size(s)的项r有P(r),我们能证明P(s) 假设对所有使得size(r)<size(s)的项r有P(r),我们能证明P(s)
则 P ( s ) 对 所 有 的 s 成 立 则P(s)对所有的s成立 则P(s)对所有的s成立
结 构 归 纳 : 如 果 对 每 个 项 s 结构归纳:如果对每个项s 结构归纳:如果对每个项s
假 设 对 所 有 s 的 直 接 子 项 r 有 P ( r ) 成 立 , 我 们 能 证 明 P ( s ) 假设对所有s的直接子项r有P(r)成立,我们能证明P(s) 假设对所有s的直接子项r有P(r)成立,我们能证明P(s)
则 P ( s ) 对 所 有 的 s 成 立 则P(s)对所有的s成立 则P(s)对所有的s成立
语义形式
求值
求值规则
- if true then t 2 else t 3 → t 2 ( E − I f T r u e ) \text{if true then }t_2\text{ else }t_3 → t_2 (E-IfTrue) if true then t2 else t3→t2(E−IfTrue)
- if false then t 2 else t 3 → t 3 ( E − I f F a l s e ) \text{if false then }t_2\text{ else }t_3 → t_3 (E-IfFalse) if false then t2 else t3→t3(E−IfFalse)
- t 1 → t 1 ′ if t 1 then t 2 else t 3 → if t 1 ′ then t 2 else t 3 ( E − I F ) \frac{t_1 → t_1'}{\text{if }t_1\text{ then }t_2\text{ else }t_3→\text{ if }t_1'\text{ then } t_2\text{ else }t_3}(E-IF) if t1 then t2 else t3→ if t1′ then t2 else t3t1→t1′(E−IF)
一步求值关系
项上满足求值的最小二元关系
多步求值关系
范式
如果没有求值规则可以作用于项
t
,则该项是范式
受阻(stuck)
如果一个封闭项是一个范式但不是一个值,则称该项受阻
haskell实现
首先定义项。由于需要限制if
的第一项类型为布尔型故添加了GADTs
扩展
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
- term
data Term a where
TmFalse :: Term Bool
TmTrue :: Term Bool
TmIf :: Term Bool -> Term a -> Term a -> Term a
TmZero :: Term Int
TmSucc :: Term Int -> Term Int
TmPred :: Term Int -> Term Int
TmIsZero :: Term Int -> Term Bool
- show terms
instance Show a => Show (Term a) where
show TmFalse = "false"
show TmTrue = "true"
show TmZero = "zero"
show (TmSucc a) = "succ " ++ show a
show (TmPred a) = "pred" ++ show a
然后定义两个方法isnumericval
和isVal
,分别用来检验当前项是值还是数字
isnumericval :: forall a. Term a -> Bool
isnumericval TmZero = True
isnumericval (TmSucc a) = isnumericval a
isnumericval _ = False
isval :: Term a -> Bool
isval TmTrue = True
isval TmFalse = True
isval t = isnumericval t
然后定义求值方法,穷举所有可能项
eval1 :: Term a -> Term a
eval1 (TmIf TmTrue t2 t3) = t2
eval1 (TmIf TmFalse t2 t3) = t3
eval1 (TmIf t1 t2 t3) =
let t1' = eval1 t1
in TmIf t1' t2 t3
eval1 (TmSucc t1) = TmSucc t1'
where
t1' = eval1 t1
eval1 (TmPred TmZero) = TmZero
eval1 (TmPred (TmSucc nv1))
| isnumericval nv1 = nv1
| otherwise = error "unsupport rules"
eval1 (TmPred t1) = TmPred t1'
where
t1' = eval1 t1
eval1 (TmIsZero TmZero) = TmTrue
eval1 (TmIsZero (TmSucc nv1))
| isnumericval nv1 = TmFalse
eval1 (TmIsZero t1) = TmIsZero (eval1 t1)
eval1 _ = error "unsupport rules"
可以看到上述求值代码有较多冗余,根据多步求值规则重写后如下
eval2 :: Term a -> Term a
eval2 a =
case a of
TmIf t1 t2 t3 ->
case eval2 t1 of
TmTrue -> eval2 t2
TmFalse -> eval2 t3
TmZero -> TmZero
TmSucc a -> TmSucc (eval2 a)
TmTrue -> TmTrue
TmFalse -> TmFalse
TmPred a ->
case a of
TmSucc b -> b
TmZero -> TmZero
_ -> error "unsupport rules"
上述实现可以看到,对于受阻项的处理都为打印错误信息。另外一种处理方式则是定义一个名为wrong
的项,用来表示受阻项,有兴趣的可自行尝试。
当然,无类型的算术表达式十分简单。接下来就将进入 λ \lambda λ演算的世界…