• 沒有找到結果。

第四章 以 AspectFun 輔助實現泛型程式設計

4.5 AspectFun 修改細節

4.5.1 Existential types

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

函數定義,修改舊有程式碼的行為會破壞程式的重用性與擴充性原則。

instance Strings (Typed a) where strings x = String?  do A

Ascii?  do B -- insert code here Others?  do C

4.4 相關的函數式剖面語言研究探討

在相關的函數式剖面語言研究中,AspectML[16]和本研究一樣採用了 Ralf Hinze 提出的 泛型作法。有別於 AspectFun 自行定義 toSpine 函數,AspectML 提供了內建的(primitive) toSpine 函數讓使用者可將自定義的資料型態轉換為 Spine 表示,與 AspectFun 相同的是 兩者皆在程式執行期間對型態進行分析,AspectFun 藉由將型態資訊程式化保留型態資 訊至執行期間處理,AspectML 藉由其語言的 typecase 機制在執行期間判斷引數型態。

和 AspectFun 不同的是 AspectML 的 toSpine 函數並不能被套用在語言內建的基本資料型 態(primitive type)上。對於設計泛型函數來說 ,函數可處理語言內建的基本資料型態顯 然較具彈性。

4.5 AspectFun 修改細節

為使 AspectFun 得以輔助實現泛型程式設計,本研究對 AspectFun 進行了部分的程式擴 充 與 型 態 推 導 系 統 的 修 改 。 擴 充 的 部 分 是 在 AspectFun 中 增 加 了 實 驗 性 質 的 Existential type,型態推導系統的修改部分為支援 Polymorphic mutual recursion,

並且對 mutual recursion 所造成的程式轉換問題以 predicate pro -pagation 處理。

4.5.1 Existential types

Existential types 是 Haskell 的現有的語言擴充之一,為了讓 AspectFun 可以實現泛型程式 設計,本研究採用了 Hinze 教授提出的 Spine 作法,然而在原有的 AspectFun 中並未支

援 Existential types 的資料型態宣告,因此我們首先必頇擴充 AspectFun 的資料型態宣 告。與 Haskell 不同的是,AspectFun 的型態推導系統中並不支援 type class 的推論,因 此在 AspectFun 的程式中使用 Existential types 的資料型態宣告時,無法將此型態變數加 上限制如 Show、ToSpine 等,在目前的 AspectFun 程式中我們僅提供最基礎的宣告方式 :

forall type-variables . data-constructor

Existential type 的使用雖然放寬了資料型態定義方式,允許程式設計者可以將型態資訊 隱藏,但是為了確保型態安全,一個被定義為 Existential types 型態的值不可以直接被函 數當作回傳值,如 :

在上述範例中,定義了一個使用 Existential types 的 Stack,我們可以藉由 push 放置任意 型態的值,但是 pop 就是一個不合法的使用方式,因為 e 是一個 Existential types,在型 態推導過程中只能判斷出 e 是某個未知的型態,我們無法在確保型態安全的限制下去直 接操作一個未知型態的值,為了判斷這一類不合法的使用,

本研究實作 Existential types 的作法首先在 AspectFun 第一階段的型態推導過程中為所有 推 導 過 程 所 使 用 的 型 態 變 數 加 上 標 籤 , 此 標 籤 的 目 的 在 判 斷 此 型 態 變 數 是 否 為 Existential types。在 AspectFun 的 parse 階段,一旦發現資料型態定義中使用了 forall 的 保留字,便會在此建構函數的型態資訊在被放入推導環境(Enviroment)前上加上對應的 標籤。

由於型態推導系統在推論完函數的型態後最後皆會呼叫 quantify 函數,quantify 函 數的作用是將系統推導出的型態資訊量化為 Type Scheme,如 :

因此我們修改 quantify 函數使其在量化前先檢查是否存在有 Existential types 標籤的型態 變數,若發現存在有 Existential types 即為一不合法的使用,型態推導系統便中斷推導過 程,否則便繼續執行原來的 quantify 工作。 被標示為 Existential types 的型態變數,但 v2 沒有標籤表示,將會導致 Existential types 標籤資訊遺失 : 的錯誤結果,為使 AspectFun 可以實現 strings 函數,本研究也放寬了 advice 使用限制,

使得 mutual recursive 函數可以作為 advice 的 pointcut,但是也造成了 AspectFun 編譯器 所轉換出的程式碼錯誤,因此我們必頇在 AspectFun 型態推導系統額外執行 predicate propagation 以便修正程式碼錯誤問題。

在放寬 advice 對 mutual recursion 的使用限制後我們發現 mutual recursion functions

ng@advice around{g} (arg :: [Int]) = if null arg then [] else proceed arg in -- f :: [a] -> [a]

為了處理 mutual recursion 函數間 predicate 資訊的傳遞問題,本研究在 AspectFun 的型態推導過程中時增加的 predicate propagation 處理步驟如下 :

3. 依據最初的 predicate 資訊與函數相依關係找出程式中 mutual recursive 函數缺 少 predicate 的部分

4.5.3 Polymorphic mutual recursion

在 strings 範例中,由於 strings 函數型態為 Typed a  [String],而 strings‟ 函數型態為 Spine a  [String],這兩函數皆為多型函數,當 strings 引數為一個整數串列 :

(Typed [1,2] (Rlist Rint …) :: Typed [Int]

我們可知此引數會被轉換為下列 Spine 編碼表示 :

App ((App (Con (Descr (:))) Typed1 Rint) (Typed [2] (RList RInt)))

當 strings‟第一次呼叫 strings 時,會將一型態為 Typed [Int]的值交由 strings 處理 App (Con (Descr (:))) (Typed1 Rint)

當 strings‟第二次呼叫 strings 時,會將一型態為 Typed Int 的值交由 strings 處理

在 AspectFun 的推導系統中,在推論遞迴函數時,函數型態會隨著推導過程從較抽象的 型態逐漸變為較具體的型態,因此當 strings‟第一次呼叫 strings 時,此時 strings 的型態 會由 Typed a -> [String]具體化為 Typed [Int] -> [String],當 strings‟第二次呼叫 strings 時,

由於 strings 函數型態已變為 Typed [Int]  [String],因此第二次呼叫傳遞的引數型態為 Typed Int 就會被推討系統判定為型態錯誤,為使 AspectFun 可以實現泛型函數 strings, 我 們除了放寬 mutual recursion 函數可作為 pointcut 外,還需修改型態推導系統使其可支援 polymorphic mutual recursion。修改作法是在 AspectFun 的兩階段推導過程時,保留最初 的函數型態,使函數型態不隨推導過程而改變,因此 strings 函數在處理 strings‟傳遞兩

‧ 國

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

次型態不同的引數值時,strings 型態始終保持為 Typed a -> [String],因此解決了多型遞 迴函數的型態錯誤。

‧ 國

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

第五章 結論與未來研究

在此研究中,我們探討了一般函數式語言機制的在程式的可重用性與可擴充性議題 上的可行性。在 Expression Problem 議題中可見,剖面除了實現 type class 帶來的程 式重用性與擴充性,proceed 機制允許程式設計者針對已定義函數重複進行擴充,type class 在擴充函數時則會破壞程式碼的重用性原則。

此外我們並藉由遞迴函數的議題展示剖面機制對遞迴函數的重用性助益,以 cflow 此剖面機制動態改變程式的執行流程,修正遞迴函數因為定義關係造成函數不易重用的 問題,展現以剖面為基礎的程式建構方法另一優勢。

在泛型程式設計的部分,延續了剖面在擴充函數上的助益,我們也利用型態程式化 的方法克服靜態型別語言在編譯期間的型態資訊不足問題,將型態資訊留至程式執行期 間處理雖然會造成額外的程式設計與效能負擔,相對於其帶來的助益是合理的可接受 的。泛型程式設計的實現促進 AspectFun 的程式設計者可建構出重用性高的程式碼。

在此論文的先前章節中,我們還有一個未探討的議題限制是個別編譯(separate compilation),在 Expression Problem 議題中,解決 Expression Problem 的最終目標 是希望舊有程式碼可以不用重新編譯。由於 AspectFun 是個靜態織入的語言,程式中定 義的剖面必頇在編譯過程中判斷是否會織入至函數中,當程式定義了新的剖面且此剖面 會影響到舊有定義的函數,在程式編譯的過程中需要知道舊有程式碼中函數的定義,編 譯器並且對 AspectFun 程式碼進行織入轉換,受限於 AspectFun 目前的型態推導系統與 編譯系統限制,目前的 AspectFun 還未能支援個別編譯,基於 Expression Problem 及 日漸龐大的應用程式系統,個別編譯有助於程式碼的重用性,未來我們期望修改 AspectFun 編譯器使其支援個別編譯。

Irwin. "Aspect-oriented programming." ECOOP '97 Object-Oriented Programming 11th European Conference, Finland (M. Aksit and S. Matsuoka, eds.). 1997, vol. 1241, pp.

220-242, New York, NY: Springer-Verlag.

[2] D. S. Dantas, D. Walker, G. Washburn, and S. Weirich, "PolyAML: a polymorphic aspect-oriented functional programming language." Proceedings of the tenth ACM SIGPLAN international conference on Functional programming, 2005, pp. 306 – 319.

[3] H. Masuhara, H. Tatsuzawa, and A. Yonezawa, "Aspectual Caml: an aspect-oriented functional language." Proceedings of the tenth ACM SIGPLAN international conference on Functional programming, 2005, pp. 320 - 330.

[4] K. Chen, S. C. Weng, M. Wang, S.C. Khoo, and C.H. Chen, "A Compilation Model for Aspect-Oriented Polymorphically Typed Functional Languages." Symposium of Static Analysis (SAS), 2007.

[5] Mads Torgersen. “The Expression Problem Revisited – Four new solutions using generics.” ECOOP’04.

[6] Jens Palsberg, C. Barry Jay “The essence of the visitor pattern” Computer Software and applications conference 1998. COMPSAC apos;98. proceedings. Aug 1998, The twenty second annual International. Volume, issue, 19-21, pp. 9 – 15.

[7] Koji Kagawa, “Polymorphic variants in Haskell”, Proceedings of the 2006 ACM SIGPLAN workshop on Haskell.

[8]Martin Odersky, Zenger, “Scalable Component Abstractions.”, Proceedings of OOPSLA 2005.

[9] Matthias Zenger, Martin Odersky. “Independently Extensible Solutions to the Expression

‧ 國

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

Problem.”, EPFL I&C technical report, March 2005, NO. 2005013

[10] Konstatin Laufer ,“What Functional Programmers can Learn from the Visitor Pattern.”

ICFP March 2003

[11] Ralf Hinze, Andres Loh, “Generic programming in 3D.”, science of computer programming, June 2009, Volume 74, Issue 8

[12] Jan de Wit. “A technical overview of Generic Haskell.” Master's thesis, department of Information and Computing Sciences, Utrecht University, 2002, INF-SCR-02-03, [13] Gilad Bracha, William Cook. “Mixin-based inheritance.”, Conference on Object Oriented

Programming Systems Languages and Applications. Proceedings of the European conference on object-oriented programming on Object-oriented programming systems ,languages, and applications, 1990, Page : 303 – 311.

[14] Andres Loh, Ralf Hinze, ”Open data types and open functions”, Proceedings of the 8th ACM SIGPLAN international conference on principle and practice of declarative programming page, 2006, pp.133-144.

[15] James Cheney, Ralf Hinze, “A lightweight implementation of generics and dynamics.”, Proceeding of the 2002 ACM SIGPLAN workshop on Haskell

[16] Geoffrey Washburn, Setphanie Weirich, “Good advice for type directed programming aspect-oriented programming and extensible generic programming.”, proceedings of the 2006 ACM SIGPLAN workshop on generic programming.

-- this program require two ghc extension to run -- -fglasgow-exts and -XOverlappingInstances data Spine a = Con (Constr a)

| forall b.ToSpine b => App (Spine (b -> a)) b data Constr a = Descr a

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) 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)

‧ 國

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

strings‟ :: Spine a→[String]

strings‟ (Con c) = [ ]

strings‟ (App f x) = strings‟ f ++strings x

‧ 國

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

附錄二

使用 type scoped advice 企圖實現泛型方法的完整 AspectFun 程式碼 (會造成 strings 錯誤結果)

data Spine a = Con (Constr a)

| forall b . App (Spine (b -> a)) b in data Constr a = Descr a in

toSpine :: a -> Spine a toSpine x = undefined in 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

int@advice around {toSpine} (arg :: Int) = Con (Descr 0) in char@advice around {toSpine} (arg :: Char) = Con (Descr „a‟) in

list@advice around {toSpine} (arg :: [a]) = case arg of [] -> (Con (Descr []))

(x:xs) -> (App (App (Con (Descr (:))) x) xs) in n@advice around {strings} (arg :: String) = [arg] in

(strings “teststr”, strings [“abc”, “def”])

--The poly-recursion patch is required to run this example data Equiv a b = Equiv (a -> b) (b -> a) in

eq_rev (Equiv a2b b2a) = Equiv b2a a2b in

eq_compose :: Equiv a b -> Equiv b c -> Equiv a c

eq_compose (Equiv a2b b2a) (Equiv b2c c2b) = Equiv (b2c . a2b) (b2a . c2b) in rep_eq :: Rep a -> Rep b -> Maybe (Equiv a b)

rep_eq (Rint a_eq_int) (Rint b_eq_int) =

Just (eq_compose a_eq_int (eq_rev b_eq_int)) rep_eq (Rchar a_eq_char) (Rchar b_eq_char) =

Just (eq_compose a_eq_char (eq_rev b_eq_char))

rep_eq (Rlist a_inner_rep a_eq_list) (Rlist b_inner_rep b_eq_list) = case (rep_eq a_inner_rep b_inner_rep) of {

Nothing -> Nothing ;

Just (inner_eq) -> Just (eq_compose a_eq_list (eq_compose

cast (Typed a rep_a) rep_b = case rep_eq rep_a rep_b of { Nothing -> Nothing ;

Just eqv -> Just (a_to_b eqv a, a_to_b eqv, b_to_a eqv) } in

castList :: Typed a -> Maybe (IsList a)

castList (Typed a rep@(Rlist inner_rep eqv)) =

Just (IsList (a_to_b eqv a) rep inner_rep (a_to_b eqv) (b_to_a eqv)) castList _ = Nothing in

toSpine :: Typed a -> Spine a toSpine x = undefined in strings :: Typed a -> [String]

strings x = strings_ (toSpine x) in strings_ :: Spine a -> [String]

strings_ (Con c) = []

strings_ (App f x) = strings_ f ++ strings x in

int@advice around {toSpine} (arg) = case cast arg (Rint equiv) of { Just (i, _, b2a) -> Con (Descr (b2a 0)) ;

_ -> proceed arg } in

char@advice around {toSpine} (arg) = case cast arg (Rchar equiv) of {

‧ 國

立 政 治 大 學

N a tio na

l C h engchi U ni ve rs it y

_ -> proceed arg } in

toList a = [a] in cons a b = a : b in

list_adv@advice around {toSpine} (arg) = case castList arg of { Just (IsList lst rep brep a2b b2a) -> case lst of {

[] -> Con (Descr (b2a [])) ;

(x:xs) -> App (App (Con (Descr (\y ys -> b2a (x : (a2b ys))))) (Typed x brep)) (Typed (b2a xs) rep)

} ;

_ -> proceed arg } in

n@advice around {strings} (arg) = case cast arg (Rlist (Rchar equiv) equiv) of { Just (b, _, _) -> [b] ;

_ -> proceed arg } in

(strings (Typed 1 (Rint equiv)), strings (Typed 'c' (Rchar equiv)),

strings (Typed "ab" (Rlist (Rchar equiv) equiv)),

strings (Typed ["ab", "cd"] (Rlist (Rlist (Rchar equiv) equiv) equiv)))

相關文件