語言的作用
語言的作用是為了交流想法,描述概念,
當前使用了什么語言,取決于我們有什么樣的需要。
為了理解詞法作用域,閉包,和continuation,
前文中,我們借助了Racket。
現在,為了理解代數數據類型(algebraic data type),多態(polymorphism),參數化類型(parameterized type),類型類(type class),我們要學習Haskell了。
編程也是如此,它是關于思想的,
編程語言只是描述這種思想的工具罷了。
非嚴格語義(non-strict semantics)
在Haskell規范中,并沒有要求使用惰性求值策略(evaluation strategy),
只是規定它是一種非嚴格的語言(non-strict language),
具體的求值策略取決于實現。
那么,什么才能叫做non-strict呢?
non-strict與lazy有什么關系呢?
還要從數學上函數的嚴格性(strictness)說起。
程序中的function與數學函數之間的關系,
是指稱語義中的內容。(指稱語義是形式語義的一種,它將每一段代碼,與一個數學對象相對應,用來研究程序的含義。
在數學上,如果一個函數對定義域中的某些參數,沒有定義值,
就稱為該函數為部分函數(partial function)。
程序中的function,對應著數學上的部分函數。
此外,程序中某一類型的全體值,也不能簡單的對應于數學上的集合,
例如,程序中的全體整數類型的值,并不對應于整數集,
因為返回整型值的function,取某些參數時,程序可能不會終止。
為了給這樣的function和返回值找到指稱,
我們給每個集合增加一個新的值:⊥
,讀作bottom。
f(⊥) = ⊥
一個數學函數,如果參數是⊥
,那么結果就一定是⊥
,
這樣的函數稱為嚴格函數(strict function)。
反之,如果參數包含⊥
,但是結果不一定是⊥
,
這樣的函數就稱為非嚴格函數(non-strict function)。
如果程序語言中function的指稱語義是非嚴格函數,
那么這樣的語言,就稱為非嚴格的語言(non-strict language)。
注:
嚴格性是指稱語義中的概念,而求值是操作語義中的概念。
并且指稱語義對于操作語義具有可靠性(soundness),
即,如果一個表達式根據操作語義求值為另一個,那么,它們必定具有相同的指稱。
因此,對于non-strict language,求值策略可能并不唯一,
對Haskell來說,GHC是最流行的編譯器,
它使用了惰性求值(lazy evaluation)。
在沒有歧義的情況下,人們常用lazy暗指non-strict,
lazy更直觀更有利于溝通。
引用透明性
An expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior.
即,引用透明性(referential transparency)指的是,
程序中的表達式總是可以用它的值來代替。
而程序中的純函數(pure function),指的是,
(1)這個函數對相同參數總是輸出相同結果
(2)這個函數沒有副作用(side effect)
因此,純函數具有引用透明性。
此外,語言采用了惰性求值,意味著表達式的求值方式是按需確定的,
所以,依靠副作用來得到計算結果就不可行了,
我們不知道計算在什么時候發生。
因此,一旦語言擁抱了惰性求值,就不得不保證引用透明性,
反之則不一定,具有引用透明性的語言,可能不必是惰性求值的。
至于,純函數是不是解決問題的最佳方式,目前尚無定論,
但至少它是為了追求優雅性,對通常編程方式的一種挑戰。
歷史上的純函數式惰性語言
在20世紀70年代末,Gerry Sussman和Guy Steele發明了Scheme,它是Lisp的一個方言,與lambda演算很相似,并支持了詞法作用域。
幾乎同時,Robin Milner為了進行自動定理證明發明了ML,其中使用了多態類型系統。
但是Scheme和ML都是基于嚴格語義的語言(strict language)。
到了80年代,人們燃起了對非嚴格語義的(non-strict),或者說是按需求值的(call-by-need)函數式語言的研究熱情。
這方面的研究理所當然的吸引了很多人,首先,函數式語言簡單而優雅,其次,惰性(lazy)與引用透明性有關,并且還可以處理無窮長的數據結構(infinite data structure)。
在80年代中期,很多研究者都想設計實現一個純函數式的(pure)惰性語言,Miranda和Lazy ML是其中的兩個例子。
1987年波特蘭FPCA'87會議之后,與會者想發明一種尚未命名的新語言,
其實一開始,人們想從一門現有的語言開始,逐漸發展迭代,比如說,使用當時最成熟的Miranda。
Miranda是David Turner的公司Research Software Limited開發的一門語言,它是惰性求值的,包含代數數據類型,使用了Hindley-Milner類型系統,從1985年起用于商業中。Miranda有很好用戶接口,對實現的支持良好,還有大量的教材。
可是,與David Turner溝通后,他拒絕了這件事。
我們想要一門用于研究語言特性的語言,因此我們決定任何人都可以擴展和修改語言本身,重新實現或者發行。但是David Turner想維持一份語言規范,讓語言具有最好的可移植性,他不想讓Miranda出現各種不同的方言。
于是,Haskell的故事就這樣開始了。
參考
A History of Haskell
Non-strict semantics
Lazy Evaluation of Haskell
Foundations for Programming Languages