第四章 以 AspectFun 輔助實現泛型程式設計
4.1 泛型程式設計
國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
第四章
以 AspectFun 輔助實現泛型程式設計
此章節將介紹函數式語言上的泛型程式設計相關研究,並闡述在 AspectFun 上如何輔 助實現泛型程式設計的最初構想與遭遇問題,最後說明本研究為了以 AspectFun 輔助 實現泛型程式設計所進行的擴充及修改的實作細節。
4.1 泛型程式設計
4.1.1 重載函數(overloaded function)
在函數式語言中,程式設計者除了可定義用來操作某特定型態的函數如 eval 函數 : eval :: Expr -> Int
也常會使用一些重載函數,所謂的重載函數是指該函數可用來操作程式中多種資料型態 的集合,例如在函數式語言中經常被使用的 show、eq 函數,show 函數可用來將多種型 態的數值轉為字串方式呈現,eq 函數是用來比較兩個相同型態的數值是否相等。在 Haskell 語言中,程式設計者通常是使用 type class 機制來實現重載函數的定義與擴充,
針對該重載函數欲處理的新型態去定義函數如何處理。
重載函數的缺點是一旦程式擴充了新的型態,對於 eq 這類會用在程式中大多數型態的 函數,程式設計者就必頇隨著型態增加而擴充函數定義,若程式中有 n 個需求如同 eq 的函數,當程式新增了一種型態,我們便必頇進行 n 次擴充,這樣的程式設計方法雖然 可以解決程式需求,但在修改程式碼的過程中可能會造成新的問題,這種顯然不是個有 效率的程式設計方法。
在 Haskell 中提供了 deriving 機制可用來解決部分的重載函數擴充問題,若程式設 計者自行定義的資料型態欲被 show 函數操作,可在該資料型態定義加上 deriving Show,如 :
‧
重複進行類似的工作。Haskell 的 deriving 機制雖然看似解決重載函數的擴充問題,但其 實 deriving 機制只適用於部分語言內建的重載函數如 Eq、Show、Read、Ord 等,針對 程式設計者自行定義的重載函數並無法使用 deriving 機制,也就是程式設計者仍然要自 行處理函數擴充的問題,例如我們欲使用自定義的 pretty 函數用來取代 show 函數:class Pretty a where pretty :: a String instance Pretty Int where
pretty i = int2String i
instance Pretty a => Pretty [a] where pretty [] = “”
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
言上支援泛型程式設計的作法。它的作法主要概念如圖 4.1 所示,程式中額外使用一中 介資料型態 Spine a,而所有泛型函數只需定義如何去操作 Spine a 此中介資料型態,而 其他的資料型態如 Int、Char、List 等則藉由轉換函數 toSpine 被轉換為中介資料型態表 示,每當新增一種資料型態時,程式設計者只需要提供此新的資料型態如何轉換 Spine a 的轉換定義,即擴充重載函數 toSpine,而原來定義好的所有泛型函數不需要重新被修 改或擴充。
圖 4. 1 : Spine 泛型作法概念
在 Hinze 教授作法中所提及的中介資料型態 Spine a,是將一般函數式語言的資料型 態宣告的結構抽象化,由於一般函數式語言的資料型態定義通常是 :
data TypeName = ConstructorName arg1 arg2 arg3 arg4 …
每個資料建構函數都會有建構函數名稱以及建構函數所需的引數值,針對資料型態定義 在語法結構上的相似性因此可以被對應到 Spine a 表示,在 Spine a 中,資料建構函數名 稱以 Con 表示,若該建構函數需要引數,則使用 App 表示將建構函數 apply 至引數上,
而語言內建的基本資料型態(primitive type)如 Int、Char 則視為無引數的資料建構函數,
‧
Existential type,Existential type 的目的是為了將型態資訊隱藏起來,在一般的資 料型態定義中,我們可以將建構函數所需的引數型態參數化 :data Tree a = Node (Tree a) (Tree a) | Leaf a
其中 a 是一型態變數,型態參數化的好處是我們不需針對各種型態的 Tree 作個別定義,
當建構函數被使用時,型態變數 a 便會依據引數型態而被綁定為某個特定型態,例如 (Leaf 5)我們可知此其型態為攜帶整數的 Tree。Existential type 是指一型態變數只 出現在資料型態定義的右邊(只在建構函數定義部分)
data Tree =Node (Tree) (Tree)
|forall b. Show b => Leaf b
當一個型態變數為 Existential type 時,型態資訊就會被隱藏,因此我們只知(Leaf 5) 型態為 Tree 但不知其攜帶的數值型態為何,以 Existential type 定義的 Tree 可以混 合攜帶各種不同型態的值如(Node (Leaf 2) (Leaf True)),這是一個合乎型態安全的 Tree。在 Haskell 中我們可以對型態變數加上部分限制,例如 Show b 即是限制型態變 數 b 是任何可被 show 函數操作的型態。在 Spine a 定義中,由於 App 是表示建構函數 名稱 apply 至引數,在非空串列的 Spine 表示中,型態變數 b 可能是 x :: a 或 xs :: [a],
因此為了確保此編碼方式可行在 App 定義中必頇使用 Existential type,在後續章節會
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
說明 Existential type 對此研究造成的問題。