你好,類型(七):Recursive type
上文我們介紹了簡單類型化 演算系統 ,並給它擴充了單位類型,
本文繼續為它添加新的特性。我們將提到let
和letrec
表達式,函數的不動點,
最後我們發現,無類型系統實際上是具有,唯一遞歸類型的一種情形。
let綁定
當寫一個複雜表達式的時候,為了避免重複和增加可讀性,
通常我們會給某些子表達式命名,其中一個常用辦法是,使用let
表達式。為此,我們要對簡單類型化 演算系統 進行擴展,
添加let
表達式的語法項,求值規則以及類型規則。新的語法:
新的求值規則:
新的類型規則:
這樣我們就可以寫出let
表達式了, ,
let
可以表示為, 。
不動點
以上let
表達式, ,
不過,通常而言, 中是可以出現 的,這時候我們就需要使用letrec
進行綁定了。
為了看清letrec
的真面目,我們用一個求階乘的例子來說明問題,
為了求解 ,
我們定義一個新函數 ,使得, ,注意到 ,所以 是 的不動點。這裡我們暫且不討論不動點的存在性和唯一性問題,
只是引入一個不動點運算元, ,它可以用來計算任意函數 的不動點。
為了達到這個目的, 必須滿足以下約束條件, 。
引入了 之後,letrec
就可以用let
表示出來了,
代數數據類型
在某些編程語言中,可以自定義遞歸類型,例如在Haskell中,
data List a = Nil | Cons a (List a)lst :: List Int lst = Cons 1 $ Cons 2 $ Nil
以上定義採用了遞歸的方式,定義了一個代數數據類型List
,
Haskell中,使用|
表示和類型(sum type),
Cons
,
用於表示各參數(a
,List a
)類型的積類型(product type),
Nil
,用來表示單位乘積類型(empty product)。(關於函數類型與指數的關係,以後有機會再介紹。)
和letrec
中的場景相似的是,List
也出現在了等式的兩邊,
因此,以上遞歸定義的List
類型可以表示為,
其中, 表示類型參數a
,
List
, 運算元用來計算類型的不動點。
無類型λ演算
在無類型 演算系統 中,定義遞歸類型, ,它滿足類型等式 。
這樣的話,無類型 演算系統 ,就可以無縫的嵌入到一個類型化的系統中去了,
該系統只存在一個類型,即遞歸類型 ,所有的項,都具有這個類型。因此,對於支持遞歸類型的系統而言,
無類型相當於具有唯一類型(Untyped means uni-typed)。總結
本文介紹了遞歸類型,引入了兩個運算元 和 ,分別用來求解函數和類型的不動點。
但是,待求的不動點是否存在,是否唯一,我們仍不能確定。要想證明這件事情並不簡單,需要補充很多額外的數學知識,
例如,良基歸納法和最小不動點定理,
此外,不動點的存在性,和遞歸的終止性也有關係。好在我們所遇到的大多數場景,都是滿足這些要求的,
因為,我們總是先確信這個解是存在的,然後再去編程,例如,fact :: Int -> Int fact 1 = 1fact n = n * fact (n - 1)
將fact
寫到等式兩邊,能讓我們更方便對fact
進行定義。
參考
Algebraic data type
Empty product Foundations for programmming languages Practical Foundations for Programming Languages下一篇:你好,類型(八):Subtype
推薦閱讀: