第四章 以 AspectFun 輔助實現泛型程式設計
4.2 實現泛型程式設計所面臨的問題
國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
說明 Existential type 對此研究造成的問題。
4.2 實現泛型程式設計所面臨的問題
在 Hinze 教授提出的泛型方法中期望將所有的重載函數都可以泛型函數替代,然而此泛 型方法被發現並無法確保所有的泛型函數的計算結果都如同程式設計者預期,其中 strings 函數便是一個代表性問題指出此泛型作法的問題。strings 函數問題是指我們希望 設計一泛型函數 strings,它可用來蒐集其引數中所有被使用的字串值如
strings “abc”
expected output [“abc”]
strings 5
expected output [ ] strings [“abc”, “def”]
expected output [“abc”, “def”]
實際執行的結果發現,當引數為一字串串列時,strings 函數的回傳值並非如同預期結果 是其引數,而是一空串列。以下我們將分別以 type class 機制與 AspectFun 的剖面機制說 明其實作方式與問題點。
4.2.1. 以 type classes 機制輔助實現泛型程式設計的實作與問題
在 Haskell 中,我們首先嘗試以 type class 機制去實現 Hinze 教授所提出的泛型方法,
由於 toSpine 函數是此方法中必需的轉換函數,藉由 type classes 我們很容易便可定義出 此重載函數並可對其進行擴充 :
class ToSpine a where toSpine :: a -> Spine a instance ToSpine Int where toSpine i = Con (Descr 0)
‧
instance ToSpine Char where toSpine c = Con (Descr „a‟)
instance ToSpine a => ToSpine [a] where toSpine [ ] = Con (Descr [ ])
toSpine (x : xs) = (App (App (Con (Descr (:))) x) xs)
在上述程式碼中可見,toSpine 函數目前可用來轉換 Int、Char、List 此三種資料型態,
在將 toSpine 函數欲操作的資料型態各別定義好後,我們便可以 type class 輔助去定義一 個泛型函數 strings,由於 strings 函數的作用是用來擷取所有資料型態中出現的字串值,
若資料型態為字串則回傳包含該字串的串列,因此我們需要針對引數為 String 型態執行 額外處理
class Strings a where
strings ::ToSpine a => a→[String]
instance Strings String where strings x = [x]
instance Strings a where
strings x = strings‟ (toSpine x)
strings‟ :: Spine a→[String]
strings‟ (Con c) = [ ]
‧
示中的”abc”與”def”字串值,然而在 strings 遞迴處理的過程中,並沒有正確呼叫 String ->[String]此型態的 strings 函數,卻是呼叫了 a -> [String]型態的 strings 函數,因此字串 值”abc”與”def”皆會被視為串列處理,造成 strings 輸出的結果不正確。
4-2-2 以 AspectFun 輔助實現泛型程式設計的實作與問題
基於在 Hasekll 中使用 type classes 輔助實現的泛型作法的錯誤結果,我們試圖使用 AspectFun 剖面機制去解決此問題,並認為以剖面實現的泛型方法相較於 type class 更為 直覺,並且可以避免函數擴充時的修改問題,然而 AspectFun 也發生了相同的結果錯誤 問題 :
strings :: a -> [String]
strings x = strings‟ (toSpine x) in strings‟:: Spine a -> [String]
strings‟ (Con c) = [ ]
strings‟ (App f x) = strings‟ f ++ strings x in
n@advice around {strings} (arg :: String) = [arg] in
在上述程式片段中,我們以 type-scoped advice n 處理當引數型態為 String 時的例外工 作。在 AspectFun 中,我們期望每次 strings 被呼叫時皆會先觸發 advice n 判斷引數型態,
但實際執行的情況在 strings‟定義中呼叫 strings 時並沒有觸發 advice n,所以在 AspectFun 上也發生 type class 的問題。
歸納兩者的原因,我們發現造成問題的原因是由於 Spine a 定義中使用了 Existential type,由於 Haskell 與 AspectFun 皆是靜態型別的語言,在靜態型別語言中型態資訊只存 在於編譯過程中,程式設計者無法直接對型態資訊進行操作例如自行根據引數的型態資 訊判斷函數接下來所要執行的運算內容為何,只能依賴型態推導系統判斷。當程式中使
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
用 Existential type,型態推論系統為了確保型態安全只能推導出較抽象的型態資訊,因 此在 strings‟定義中呼叫 strings 時推導系統只能判斷出此時傳給 strings 的引數型態是某 個未知型態 a 而非具體的 String 型態,導致 advice n 不會被觸發,欲使 AspectFun 可以 輔助實現泛型程式設計,我們需要解決 Existential type 造成型態資訊不足問題,使得 advice n 可以被織入至 strings。