• 沒有找到結果。

以AspectFun探討模組化型態擴充與泛型程式設計 - 政大學術集成

N/A
N/A
Protected

Academic year: 2021

Share "以AspectFun探討模組化型態擴充與泛型程式設計 - 政大學術集成"

Copied!
52
0
0

加載中.... (立即查看全文)

全文

(1)國立政治大學資訊科學系 Department of Computer Science National Chengchi University 碩士論文 Master’s 治Thesis. 立. 政. 大. ‧. ‧ 國. 學. 以 AspectFun 探討模組化型態擴充與泛型程式設計. y. Nat. er. io. sit. A study on modular type extension and generic. n. programming using AspectFun al v i n Ch engchi U. 研 究 生:陳政宏 指導教授:陳. 恭. 中華民國九十九年二月 February. 2010.

(2) 以 AspectFun 探討模組化型態擴充與泛型程式設計. A study on modular type extension and generic programming using AspectFun. 研 究 生: 陳政宏 指導教授: 陳. 立. Student:Cheng-Hung Chen. 恭. Advisor:Kung Chen 治 政 大. ‧ 國. 資訊科學系. 學. 國立政治大學. Nat. al. A Thesis. er. io. sit. y. ‧. 碩士論文. n. v i n submitted toCDepartment of Computer Science hengchi U National Chengchi University. in partial fulfillment of the Requirements for the degree of Master in Computer Science. 中華民國九十九年二月 February 2010.

(3) 以 AspectFun 探討模組化型態擴充與泛型程式設計. 摘要. AspectFun 是一個語法近似 Haskell 語言的函數式剖面導向語言。本論文中探討 AspectFun 在模組化型態擴充以及泛型程式設計上可扮演的角色。研究首先比較剖面與. 政 治 大 名的 Expression Problem 立 作為代表(程式語言機制如何確保程式在擴充資料與運算函數 Haskell 語言的 type class 在處理型態擴充需求議題的可行性,型態擴充議題我們以著. ‧ 國. 學. 的過程中,不需要修改舊有的程式碼,並能確保程式的型態安全)。 我們接著會探討剖面如何以模組化方式實現泛型程式設計。泛型程式設計是指函數. ‧. 接收一額外的型態引數,且函數所執行的運算是依據此型態引數結構來進行的。型態引. sit. y. Nat. 數是用來表示函數所處理的引數或其回傳值型態為何,但型態引數在函數定義中可能是. al. er. io. 明確定義的或者隱含的。在此研究中會展示以剖面實現的泛型程式設計更優於使用 type. v. n. class。此外,本研究為使 AspectFun 可以實現泛型程式設計方法,亦在 AspectFun 擴. Ch. engchi. 充了 Existential types 與多型互遞迴函數。. i. i n U.

(4) A study on modular type extension and generic programming using AspectFun. Abstract. AspectFun is an aspect.oriented functional language with a Haskell.like syntax. This thesis present an study on modular type extension and generic programming using. 政 治 大. AspectFun. First, we compare the feasibility of using aspects and Haskell's type classes to. 立. address the type extension requirements as stated in the famous expression problem (which. ‧ 國. 學. calls for language mechanisms that can support type.safe program extension in both the dimensions of data types and associated operations, yet neither code duplication nor code. ‧. rewriting is required.). y. Nat. io. sit. Second, we investigate how to use aspects to support generic programming in amodular. n. al. er. manner. Generic programming means a form of programming in which a function takes a type. i n U. v. as argument, and its behavior depends upon the structure of this type. The type argument,. Ch. engchi. which may be explicit or implicit, represents the type of values to which the function is applied, or which the function returns. We show that aspects can do better than type classes in supporting generic programming. In particular, we extend AspectFun with existential types and polymorphic mutual recursion to achieve such a result.. ii.

(5) 致謝詞 感謝指導教授陳恭老師的細心教導與督促,使得我可以完成此論文。從論文題目的方向 一直到論文的完成,老師一直給予我細心的指導與督促,在此求學的過程中,我除了學 習到老師嚴謹的研究精神與態度,此外老師也教導了我許多待人處事應有的道理。 在此我還要感謝王萌(Wang meng)先生在研究過程中遇到瓶頸時給予的建議,幫助 我克服研究上的問題。此外我要特別感謝書鈞,在研究上幫助我解決許多實作上的問. 政 治 大 凱這些實驗室的成員在研究過程中給予我的關懷與激勵,使得我有持續的動力可以完成 立. 題,直到此研究順利的完成。感謝佳瑩學長、朝傑學長、啟典、于育、尚倫、名宏、文. 此論文。. ‧ 國. 學. 感謝我的家人,特別是我的父母,在我求學的期間給予的耐心的支持與包容,還有. ‧. 生活上提供給我的幫助,使得我可以沒有後顧之憂,順利完成我人生中一個重要的求學. y. Nat. 階段,希望能與他們一起分享完成論文後的喜悅。. er. io. sit. 在此論文完成之時,我要再次感謝老師、同學、朋友、家人賦予我的有形的與無形 的幫助,在政大資科求學將會是我人生一個印象最深刻的歷程。. n. al. Ch. engchi. iii. i n U. v.

(6) 目錄 第一章 導論 .............................................................................................................................. 1 1.1 研究動機 .................................................................................................................... 1 1.2 研究目標 .................................................................................................................... 2 1.3 論文之成果 ................................................................................................................ 3 1.4 論文之限制 ................................................................................................................ 3 1.5 論文章節架構 ............................................................................................................ 3. 政 治 大 2.1 程式的可重用性 立 (reusability)及擴展性(extensibility) .............................. 5. 第二章 背景介紹 ...................................................................................................................... 5. ‧ 國. 學. 2.2 AspectFun 介紹 ......................................................................................................... 5 2.2.1 AspectFun 語言特色與基礎語法介紹 ......................................................... 6. ‧. 2.2.2 Type-scoped advice .................................................................................... 7. sit. y. Nat. 2.2.3 Cflow pointcut ............................................................................................ 8. al. er. io. 2.2.4 data+ .............................................................................................................. 8. v. n. 2.2.5 AspectFun 的織入系統介紹 ......................................................................... 9. Ch. engchi. i n U. 第三章 探討 AspectFun 的程式重用性與擴充性 ................................................................ 12 3.1 The Expression Problem ...................................................................................... 12 3.2 Open datatype / Open function ........................................................................ 13 3.3 AspectFun 的程式擴充機制在 Expression Problem 的應用 ............................. 15 3.4 相關程式重用與擴充議題探討 .............................................................................. 16 第四章 以 AspectFun 輔助實現泛型程式設計 .................................................................... 19 4.1 泛型程式設計 ........................................................................................................... 19 4.1.1 重載函數(overloaded function) ............................................................ 19 4.1.2 泛型程式設計與相關研究介紹 .................................................................. 20. iv.

(7) 4.2 實現泛型程式設計所面臨的問題 .......................................................................... 23 4.2.1 以 type classes 機制輔助實現泛型程式設計的實作與問題 ................. 23 4.3 以 AspectFun 輔助實現的泛型程式設計 ............................................................... 26 4.4 相關的函數式剖面語言研究探討 ........................................................................... 30 4.5 AspectFun 修改細節 ............................................................................................... 30 4.5.1 Existential types .................................................................................... 30 4.5.2 Predicate propagation ............................................................................ 32. 政 治 大 第五章 結論與未來研究 ........................................................................................................ 36 立 4.5.3 Polymorphic mutual recursion .............................................................. 34. 參考文獻 .................................................................................................................................. 37. ‧ 國. 學. 附錄 .......................................................................................................................................... 39. ‧. 附錄一 .............................................................................................................................. 39. y. Nat. 附錄二 .............................................................................................................................. 41. n. al. er. io. sit. 附錄三 .............................................................................................................................. 42. Ch. engchi. v. i n U. v.

(8) 圖目錄 圖 2. 1 : AspectFun Syntax ........................................................................................................ 6 圖 4. 1 : Spine 泛型作法概念 ................................................................................................. 21. 立. 政 治 大. ‧. ‧ 國. 學. n. er. io. sit. y. Nat. al. Ch. engchi. vi. i n U. v.

(9) 第一章 導論 1.1 研究動機 自從 1997 年 Kiczales et al[1]提出剖面導向程式設計(Asepct-Oriented Programming, AOP)的方法後,許多的程式語言與軟體工程的學者專家紛紛投入這方面的研究,剖面 導向程式設計是繼物件導向程式設計後,所興起的一種新的模組化程式設計方法。傳統. 政 治 大 便維護與修改,然而系統中經常會有某些非功能性的需求如記錄、安全性檢查等,這些 立 程式設計者在使用物件導向語言建構系統的作法,會依據系統功能切割成不同的模組以. 橫跨性關注(cross-cutting concerns)的程式碼會散布在各個模組中,使得程式難以維. ‧ 國. 學. 護,AOP 的目的是將把散布在各個模組的程式碼中收集並獨立成為一個模組,提供有效. ‧. 的封裝與模組化支援,透過織入器(weaver)將程式碼插入各個模組化。. y. Nat. 在眾多的剖面導向語言中,主要可分基於物件導向語言與基於函數式語言兩類,在. er. io. sit. 物件導向語言類別中,其中以 AspectJ 此剖面導向語言最為知名並被廣泛應用在實務 上,許多剖面語言研究大多以 AspectJ 作為研究對象,在函數式語言方面,亦有許多相. al. n. v i n Ch 關的實驗性語言如 Poly AML[2]、Aspectual Caml[3]以及 AspectFun[4]等函數式剖面 engchi U. 導向語言。. 在程式語言的研究中,程式的可重用性(reusability)與可擴充性(extensibility)一直是 個重要研究議題,研究者的目標希望使用程式語言設計出的程式碼能夠具備此兩項特 性。在程式的可擴充性議題中,對於各種程式語言不同的程式建構方法與語言特有機 制,Expression Problem 經常被視為衡量程式擴充性的標準,Expression Problem 最 初在物件導向語言中被提出,探討程式語言在不更動程式中原有資料組成及運算函數的 限制下擴充程式所發生的問題,針對使用物件導向語言解決 Expression Problem,有學 者提出以泛型[5]及 Visitor Pattern[6]的解決方法;函數式語言方面則有 Caml 與 Haskell 的 polymorphic variant 機制[7]。最近物件導向與函數混合式語言 Scala[8] 1.

(10) 也提出他們的作法[9]。但是在剖面導向語言方面,相關的研究則較少。在程式的可重 用性議題中,有研究[10]指出了函數式語言中遞迴函數不易被重用的問題,並提出以高 階函數(high-order function)模擬物件導向語言程式碼重用機制,避免遞迴函數在其 定義中寫死(hard code)遞迴呼叫自己造成的無法重用問題,針對遞迴函數的重用性議 題相關的剖面導向語言研究也甚少提及。因此我們希望使用 AspectFun 特有的語言機制 去探討一套以剖面為基礎的程式建構方法,以便能設計出具被可擴充性與可重用性的程 式。. 政 治 大 泛型程式設計泛指我們針對程式中的資料結構、演算法等內容概要上的相似性找出一套 立. 延續程式的重用性議題,泛型程式設計也是一種被視為促進程式碼重用性的概念,. 抽象化與系統化的程式建構方式,使得程式設計者可以重複使用此程式。在眾多程式語. ‧ 國. 學. 言中各有不同的泛型實現作法,在函數式語言方面,Ralf Hinze 教授提出了在 Haskell. ‧. 上 實 現 泛 型 程 式 設 計 的 作 法 [11] , 函 數 式 語 言 亦 有 相 關 的 實 作 如 Generic. y. Nat. Haskell[12]。支援泛型的程式語言允許程式設計者可以針對程式中結構相同但型態不. er. io. sit. 同的運算定義相關的泛型程式,而不需再針對不同的型態作個別定義。在大多數的程式 中,使用以型態為導向的程式設計方法(type directed programming, TDP)雖可滿足程. al. n. v i n Ch 式設計者大多數的需求,但也使得相似的程式碼需要一再被重覆撰寫,造成程式難以維 engchi U 護,而泛型程式設計便可用來作為 TDP 的互補。在 AspectFun 中,雖不支援 type class. 的推導,但藉由 type-scoped advice 亦可實現 TDP,因此我們希望擴充 AspectFun 使其 支援泛型程式設計,促進 AspectFun 程式的重用性。. 1.2 研究目標 本論文以 AspectFun 為基礎,探討該程式語言的程式重用與擴充機制,並以 Expression Problem 及遞迴函數作為探討議題,說明如何使用 AspectFun 剖面機制撰寫具備可擴充 性及可重用性的程式。本研究的另一目標在以 AspectFun 輔助實現泛型程式設計,藉由. 2.

(11) 修改 AspectFun 的型態推導規則並擴充其資料型態宣告,使得 AspectFun 可以輔助實現 泛型程式設計,並能確保程式的型態安全。. 1.3 論文之成果 本論文成果主要為下列兩項 . 使用 AspectFun 的剖面機制探討函數式語言上的程式重用與擴充性問題, 並說明如何以剖面機制解決程式不易擴充的問題。另外針對遞迴函數的重 用性問題,也指出以剖面機制 cflow 取代先前研究方法的優勢。. . 政 治 大 計,本研究擴充了 立 AspectFun 的資料型態宣告使其支援 Existen -tial Type,. 在 AspectFun 語言中輔助實現了泛型程式設計。為輔助實現泛型程式設. ‧ 國. 學. 並修改了 AspectFun 在多型遞迴函數上型態推導規則,以及修正 mutual recursion 所造成的 predicate 傳遞問題。. ‧. 1.4 論文之限制. y. Nat. sit. 由於 AspectFun 不支援 type class 的機制,因此 AspectFun 編譯器無法編譯使用 type class. n. al. er. io. 的函數,AspectFun 另有提供單型(monomorphic)函數替代多型(polymorphic)函數,例如 inteq、chareq 用來替代(==)。. Ch. engchi. i n U. v. 另外 AspectFun 原始碼使用了 GHC 特有的擴充及函式庫,所以無法使用其它的 Haskell 編譯器進行編譯。AspectFun 原始碼僅限 GHC 6.8 之後的版本可以進行編譯。針 對本論文中第四章節所提及的 AspectFun 範例需要使用額外的 patch 才能完成編譯,其 餘的範例程式碼則不需使用。. 1.5 論文章節架構 第二章介紹程式擴充性與重用性概念,以及本研究所使用的 AspectFun 語言之語法定 義、剖面織入規則、type-scoped advice、cflow 和實際應用範例。 第三章介紹程式語言處理 Expression Problem 的問題所在,以及重用遞迴函數程式 碼的問題點,除了以 AspectFun 範例程式說明其語言機制如何處理此兩項議題,並比較 3.

(12) AspectFun 的 type-scoped advice 與 type class 的差異。 第四章介紹如何在 AspectFun 上輔助實現泛型程式設計,並說明在實現泛型程式設 計過程中遭遇的問題及其解決作法,最後並與相關函數式剖面導向語言研究 Aspect ML 比較成果差異性。 第五章總結本研究的成果及描述未來可進行的方向。. 立. 政 治 大. ‧. ‧ 國. 學. n. er. io. sit. y. Nat. al. Ch. engchi. 4. i n U. v.

(13) 第二章 背景介紹 2.1 程式的可重用性 (reusability)及擴展性(extensibility) 程式語言的目標是希望提供模組化的程式建構方法以便程式設計者設計出重用性高且 具備可擴充性的程式碼,重用性高的程式碼是指程式碼不需修改和重新編譯即可被新程 式碼引用且正確運作,重用程式碼的好處是避免了程式設計者重複同樣的工作並且免於. 政 治 大 功能模組,而新增的功能模組日後亦可繼續擴充。 立. 錯誤,可讓程式設計者減少開發程式的時間;程式的擴充性可讓現存的系統容易增加新. 在主流的物件導向語言中,強調將資料與其相關的運算函數封裝為一個模組單位,. ‧ 國. 學. 藉由繼承(inheritance)及委派(delegation)來達到程式碼重用的目的。此外,近幾年來在物. ‧. 件導向語言程式相關的程式擴充性研究中 Mixin[13]、Visitor Pattern 等也常被視為促進. y. Nat. 程式碼重用的方法之一,Visitor Pattern 作法是將類別(class)中所有的方法(method)抽離. er. io. sit. 至外部的 Visitor 類別,一旦程式設計者希望父類別的方法有所擴充時,可以藉由繼承實 現擴充,Visitor Pattern 適用於資料不變動的狀況;而 Mixin 可視為繼承機制的延伸,它. al. n. v i n Ch 允許程式的模組單位在定義時不需先指定其繼承的父類別)而是延後至該模組實際被使 engchi U 用時才指定. 在 Scala 語言中可使用其實現的 Mixin 的建構機制(在 Scala 中稱之為 trait 的模組化 建構單位);在函數式語言中,程式碼的重用通常是藉由高階函數(High order functions) 達成,高階函數是指一個函數接收其它函數作為引數或者將函數當作回傳值,常見的高 階函數如 map、fold 等。此外亦有許多相關研究探討如何在函數式語言中導入物件導向 語言的程式碼重用機制以便設計出更具擴充性的程式碼。. 2.2 AspectFun 介紹. 5.

(14) 由於本研究目的之一在探討 AspectFun 此函數式剖面導向語言在程式擴充性與泛型程式 設計上的支援與應用,此章節將簡略說明 AspectFun 的語言特色並以範例介紹其剖面機 制及相關應用。. 2.2.1 AspectFun 語言特色與基礎語法介紹 AspectFun 是一個多型(Polymorphic)及靜態型別(statically typed)的程式語言,其語 法近似於 Haskell 語言,在 AspectFun 程式中使用者可以定義多型函數並且在編譯過程 中會對程式進行型態推導檢查程式是否有型態錯誤問題。透過 AspectFun 編譯器可將以. 政 治 大 AspectFun 程 式 中 主要 分 為 三 個部 分 : 函數 宣 告 、 剖面 宣 告及程 式 主 體 (main 立. AspectFun 定義的語法(圖 2.1)撰寫的程式碼轉換成標準可執行的 haskell 程式。. expression)。函數宣告如同 Haskell 語言,剖面宣告由剖面名稱、Pointcut 以及剖面. ‧ 國. 學. 定義(body)組成,Pointcut 為程式設計者指定剖面所會影響的函數,AspectFun 與. ‧. AspectJ 語言的剖面宣告有所不同,Pointcuts 宣告與 Advice 中的程式為一個完整結. y. Nat. 構,不可各自獨立分開宣告。本語言僅能抓取在頂層宣告的函數或 advice,區域 let. n. al. er. io. sit. 定義之函數無法被 advice 影響,advice 僅影響頂層定義的函數。. Ch. engchi. i n U. 圖 2. 1 : AspectFun Syntax. 6. v.

(15) 下列程式碼為一簡單的 AspectFun 程式,其中宣告了一個剖面模組 n,n 在函數 f 被呼 叫時將會被觸發並取代 f 執行運算,在 n 定義中所使用的 proceed 為 AspectFun 定義的 保留字,其功能等同於 AspectJ 之 proceed(),用於呼叫此剖面所影響的函數(即範例中 的 f),而剖面的引數 arg 視為受影響函數的引數(即 f 定義中的引數 x)。 -- aspect definition n@adivce around {f} (arg) = proceed arg in -- function definition f x = x in --main expression (f 2). 政 治 大. 立. 而在 AspectJ 常見的 before 與 after advice 都可透過 AspectFun 中的 around 去模. ‧ 國. 學. 擬 :. ‧. my_before@adivce around {f} (arg) = { println “before f get called” ; proceed arg }in. sit. y. Nat. 2.2.2 Type-scoped advice. al. n. 據引數的型態才被觸發 :. er. io. Type-scoped advice 為 AspectFun 所提供的另一種剖面宣告,其目的在限制 advice 是依. Ch. engchi. i n U. v. n1@advice around {f} (arg) = { println “arg is any type” ; proceed arg} in n2@advice around {f} (arg :: [a]) = { println “arg is list” ; proceed arg } in n3@advice around {f} (arg :: [Int]) = { println “arg is list of int” ; proceed arg } in. f x = x in (f 3, f [„a‟, „b‟, „c‟], f [1, 2, 3]). 7.

(16) 在上述程式中,定義了 n1、n2、n3 三個 advice,其中 n1 在每次 f 被呼叫時都 會被觸發,n2 只有當 f 的引數型態為任意型態的串列時才會被觸發,而 n3 只 有當 f 的引數型態為整數串列時才會被觸發。 在先前未提及的是,AspectFun 是一個靜態織入的剖面導向語言,在程式中宣告的 advice 在編譯過程中就必頇決定函數呼叫是否會觸發 advice,在編譯過程中,AspectFun 會推導出函數與 advice 的形態,若 advice 的型態比 pointcut 中指定的函數型態更為 抽象時(general),此 advice 便會織入至函數中,一旦函數被呼叫時 advice 便會被觸 發。. 2.2.3 Cflow pointcut. 立. 政 治 大. AspectFun 也提供了類似 AspectJ 的 cflow 功能,cflow 是指程式執行時流程控制,其. ‧ 國. 學. 目的近似於 type-scoped advice,限制程式在某個特定的執行流程下才會觸發 advice,. ‧. 如 :. (f 1, g 2). sit. al. er. proceed arg } in. n. g x = f x in. io. f x = x in. y. Nat. cn@advice around {f + cflow(g)} (arg) = { print “trace : f call g” ;. Ch. engchi. i n U. v. 在上述範例中,定義了函數 f 與函數 g,在 g 的定義中將會呼叫 f,而 advice cn 的 pointcut 部分使用了 cflow,f + cflow(g) 表示當函數 g 在執行的情況下且 f 被呼叫時,advice 將 會被觸發,因此在程式主體中,當程式執行了 f 1,cn 將不會被觸發,當程式執行了 g 2 時滿足 cn 的 pointcut 定義,cn 將會被觸發。 AspectFun 提供了相當具有彈性的 cflow 機制與語法,我們也可改寫原來的 pointcut 定義為 f – cflow(g),限制 advice 在 f 被呼叫時且 g 不被執行的狀況下才會觸發。. 2.2.4 data+ 基於對舊有程式的擴充需求,除了一般函數式語言的資料型態宣告方式,AspectFun 提 8.

(17) 供了一特殊的資料型態宣告方式 data+,data+的目的在減少程式設計者在擴充新的資料 型態定義時直接修改舊有資料定義的程式碼 : data Shape = Circle Int in. drawShape :: Shape -> ( ) drawShape (Circle r) = drawCircle r in 在上述範例中可見,程式碼中定義了一資料型態 Shape 及函數 drawShape,drawShape. 政 治 大 計者想要對原有的程式繼續擴充時,使用 data+可以避免程式設計者直接修改舊有的資 立. 可將不同的 Shape 輸出至螢幕顯示,由於目前只定義了一種 Shape 為 Circle,當程式設. 料型態定義 :. ‧ 國. 學. data+ Shape = …| Square Int Int in. ‧. 由於原有的資料型態 Shape 現在以被擴充增加了 Square,原有的函數定義也必頇同樣跟. y. Nat. 著擴充,否則便會造成程式在執行時發生錯誤,在 AspectFun 中可以藉由宣告 advice 來. er. io. sit. 達到擴充函數定義的目的 : square@advice around {drawShape} (arg) = case arg of. n. al. v i n Ch Square w h -> drawSquare w h engchi U _. -> proceed arg in. 2.2.5 AspectFun 的織入系統介紹 AspectFun 是一個靜態織入的剖面導向語言,靜態織入是指程式在編譯過程中就要決定 advice 是否要被觸發,例如一個 AspectFun 程式 : n@advice around {f} (arg) = proceed arg in f x = x in g x = f x in (g 5, g „a‟) 在編譯後,AspectFun 程式碼將會被轉換為可執行 Haskell 程式碼如下所示 : 9.

(18) n proceed arg = proceed arg fx=x g x = (n f) x main = do putStrLn $ show $ (g 5, g „a‟) 其中 advice n 將會被轉換為一高階函數宣告,proceed 是高階函數的參數,目的在讓 advice 定義中所使用的 proceed 保留字可以被代換為 pointcut 中的函數,arg 參數的作用類似 proceed。在原先的 AspectFun 程式中,每次呼叫函數 f 將會觸發 advice n,即 n 會織入. 政 治 大 而這就是 AspectFun 用來實現靜態織入的程式碼轉換方法。 立. 至 f 中,在轉換後的 Haskell 程式碼的函數 g 定義中,((n f) x)即是表示 n 織入至 f 中,. 除了一般的 advice 宣告,我們知道 AspectFun 也提供了 type-scoped advice 宣告控制. ‧ 國. 學. advice 的觸發條件,由於 type-scoped advice 必頇依據函數引數的實際型態判斷織入與. ‧. 否,因此在編譯過程中無法直接將所有的函數 f 呼叫一律轉換為(n f) ,如 :. y. sit. al. n. (g 5, g „a‟). io. g x = f x in. er. f x = x in. Nat. n@advice around {f} (arg :: Char) = proceed arg in. Ch. engchi. i n U. v. 在上述程式中,由於 g 的型態是 a -> a,而 advice n 只有當引數為 Char 型態時才會被觸 發,在推導至 g 時,我們只知道 g 的引數是某個任意型態 a,無法判斷此時呼叫 f 是否 會觸發 advice,因此我們需要型態資訊來輔助判斷函數是否要被織入,而 predicate 就是 AspectFun 在輔助用來判斷 advice 是否織入的額外型態資訊。 當程式中使用 type-scoped advice 時,由於某些函數定義的型態較 type-scoped advice 定義的型態較為抽象,在推導至此函數無法立即判斷織入與否,此時函數會形成 predicate,直到推導至其他函數或程式主體確定型態資訊足夠時,才判斷是否織入。在 前述程式中,由於 f 的型態是 a -> a,暫時無法判斷 n 是否織入,此時 f 會形成 predicate, 而 g 的定義中會呼叫 f,因此 g 會帶著 predicate f,直到推導至程式主體時確定 f 的引數 10.

(19) 值是為 Char 型態,才對 f 進行織入。此 AspectFun 程式最後會被轉換為下列可執行的 Haskell 程式碼 : n proceed arg = proceed arg fx=x g df x = df x main = do putStrLn $ show $ (g f 5 , g (n f) „a‟) 其中 g 帶著 predicate f,因此原來的函數 g 定義會被轉換為 g df x = df x,在推導至程式. 政 治 大 5 不符合 n 被觸發的條件,因此只傳遞未被織入的 f。 立. 主體中的 g „a‟時知道 f 會觸發 advice n,便傳遞織入過的 f 函數(n f);在程式主體中的 g. ‧. ‧ 國. 學. n. er. io. sit. y. Nat. al. Ch. engchi. 11. i n U. v.

(20) 第三章 探討 AspectFun 的程式重用性與擴充性 3.1 The Expression Problem 對於各程式語言不同的程式建構方法,我們需要一個公認有效的衡量標準(benchmark) 來評估該程式語言程式建構機制的優劣,而 Expression Problem 就是個經常被用來衡量 程式語言建構機制優劣的問題。Expression Problem 是指在實作一個程式語言時,因為 data 及 operation 兩個方向在擴充的過程中會因為程式語言的擴充機制受限,導致程式設. 政 治 大. 計者需要修改舊有程式碼的問題。Expression Problem 所要解決的問題在於:. 立. 擴充新的 data / operation 時,舊有的程式碼不需修改和重新編譯, 新的程式. 學. ‧ 國. . 碼不會重複舊有程式碼。 . 程式碼在通過編譯器的型態推導系統後,在執行期間(runtime)不會產生型態錯. ‧. 誤。. y. Nat. sit. 在物件導向語言中,我們透過繼承便可輕易擴充新的 data,而擴充新的 operation. n. al. er. io. 卻需要修改所有相關的類別(class),而 Visitor Pattern 的作法是將類別中的方法( Method). i n U. v. 抽離至外部成為 Visitor 介面的一種設計方法,針對需要新增的 operation 程式設計者只. Ch. engchi. 需定義新的類別並實作 Visitor 介面即可擴充 operation,在 data 不變動的前提下使用 visitor pattern 有助於程式的模組化建構,但使用 Visitor Pattern 也相對限制了物件導向語 言擴充 data 的簡易性;而函數式語言則與物件導向語言相反,擴充 operation 容易但擴 充 data 時則需要修改原有程式碼,如下列範例程式中: module Lang where data Expr = Num Int |Plus Expr Expr. eval (Num i) = i eval (Plus e1 e2) = (eval e1) + (eval e2) 12.

(21) Lang 模組包含已定義好的部分 Expr 資料型態以及 eval 函數,若我們欲在不修改 Lang 模組的限制下新增 toString 函數,在 Haskell 中此函數可被定義在與 data 不同模組中, 而原有的模組不需修改。 module Main where import Lang toString (Num i) = inttostring i toString (Plus e1 e2) = toString e1 ++ “ + ” ++ toString e2. 政 治 大. 但是當 data 需要擴充時,便需要修改 Expr 所在模組,且操作 Expr 相關的函數也需要針 對新的 data 擴充原有的定義。. 立. 學. ‧ 國. | Plus Expr Expr | If Expr Expr Expr. ‧. ... (略). io. …(略). y then (eval e2) else (eval e3). er. Nat. eval (If e1 e2 e3) = if (iszero $ eval e1). sit. data Expr = Num Int. al. n. v i n toString (If e1 e2 e3) = “if” C ++ toString e1 ++ “then” h e n g c h i U ++ toString e2 ++ “else” ++ toString e3. 3.2 Open datatype / Open function 針對函數式語言因擴充 data 時造成已存在程式必頇修改的問題,相關研究[14]提出了 open datatype 及 open function 概念,data 與 function 若被宣告為 open,程式設計者可在 不修改原有模組的情況下擴充已存在的 data 與 function 的定義,但此方法需要修改 Haskell 語言才可實現 open 機制。在此研究中也提及另一種實作方式試圖以 Haskell 現 有的程式機制解決 data 擴充問題,它的作法是將 Expr 中的每個資料建構函數(data constructor)提升為獨立的資料型態(datatype): data Num = Num Int 13.

(22) data Plus a b = Plus a b 由於 Num 與 Plus 經過改寫後已成為不同的型態,原有的 eval 函數也必頇將函數型態放 寬為 a  Int,在 Haskell 中的 type class 機制提供程式設計者定義一組多載(overloading) 函數,將 Num 及 Plus 宣告為 class Eval 的 instance,因此 eval 可以操作這兩種不同型態 的資料 class Eval a where eval :: a -> Int. 政 治 大. instance Eval Num where eval (Num i) = i. 立. instance (Eval a, Eval b) => Eval (Plus a b) where. ‧ 國. 學. eval (Plus a b) = eval a + eval b. ‧. 每當需要增加新的 operation 時,只需要定義此 operation 的 class 及其 instances. sit. y. Nat. class ToString a where. io. er. toString :: a -> String instance ToString Num where. n. al. …(略). Ch. engchi. i n U. v. instance (ToString a, ToString b) => ToString (Plus a b) where …(略) 使用此實作方式雖可解決 data 擴充問題,且透過定義新的 instance 也避免了在 data 擴充 後相關函數定義需要重新修改的狀況 data If a b = If a b b instance (Eval a, Eval b) => Eval (If a b) where eval (if e1 e2 e3) = …(略). 14.

(23) 3.3 AspectFun 的程式擴充機制在 Expression Problem 的應用 我們已知函數式語言在處理 Expression Problem 時的兩個問題點 . 當 data 需要擴充時,data 定義的程式碼部分就需要被修改. . 在 data 擴充後,用來操作該 data 的相關函數也需要進行修改. 在 3.2 節中提到了使用個別獨立的 datatype 及 type classes 克服函數式語言不易增加 data 的困難,我們希望藉由 AspectFun 解決 Expression Problem。 針對資料型態 Expr 的部分,我們參考研究[14]中所提出的資料建構函數提升為獨立 的資料型 :. 立. data Num = Num Int in. 政 治 大. ‧ 國. 學. data Plus = Plus a b in. ‧. 針對函數擴充部分,在 Haskell 中我們知道使用 type class 機制可以擴充新的函數,. sit. y. Nat. 然而 AspectFun 中並不支援 type classes 機制,我們無法使用 type class 來擴充函數定義。. al. er. io. 若將 type classes 中的 class 視為物件導向語言中的抽象類別(abstract class),且將 instances. v. n. 視為繼承此抽象類別的子類別(subclass)並實作其父類別所定義的抽象方法(abstract. Ch. engchi. i n U. method),我們引用此繼承概念並透過 AspectFun 提供的 type-scoped advice 機制,模擬 這物件導向語言常見的實作方法。首先將 eval 函數定義為 undefined eval :: a -> Int eval e = undefined in 此未定義的 eval 函數可視為是一抽象類別,undefined 為 AspectFun 內建的函數,此時 若呼叫 eval 函數將會產生執行錯誤(runtime error)。接著透過 advice 來擴充 eval 函 數的定義,num 與 plus advices 在此處可視為是繼承 eval 的兩個子類別 num@advice around{eval}(e :: Num) = case e of {Num i -> i;. 15.

(24) _. -> proceed e} in. plus@advice around{eval}(e :: Plus a b) = case e of {Plus e1 e2 -> eval e1 + eval e2; _. -> proceed e} in. 在此程式中可以發現 advice 與 type class 在擴充函數時的差異性,使用 advice 避免了 type class 複雜的相依關係,advice 沒有 class 間的繼承關係,使用 advice. 政 治 大 特定型態的函數引數重複定義進行擴充,例如我們想要知道在每次 eval If 運算式時執 立 在擴充函數時更直覺簡易,且 advice 與 type class 的最大差異在於 advice 可針對某. 行的是 then 或者 else 部分的程式,我們可額外定義一個 advice log. ‧ 國. 學 ‧. log@advice around {eval} (e :: If a b c) = case e of { (If e1 e2 e3) -> if (iszero $ eval e1) then {println “eval else”; proceed e} else {println “eval then”; proceed e}; -> proceed e} in. n. al. er. io. sit. y. Nat _. i n U. v. 而 type class 針對每一種型態只能定義一個對應的操作,一旦需要增加新的功能只能修 改舊有的程式碼。. Ch. engchi. 3.4 相關程式重用與擴充議題探討 在函數式語言中,遞迴函數佔有相當重要的地位,我們經常會使用遞迴函數來處理遞迴 宣告的資料型態,例如常被用來實作檔案系統的 Tree,如下列範例所示 data Tree a = Node (Tree a) csum | csum. (Leaf i) = (Node (l,. (Tree a). | Leaf. csum. l + csum. a. i. r)) =. r. csum 用來計算 Tree 中所有 Leaf 的總合。為了實現程式碼重用,我們藉由 csum 函數來 16.

(25) 定義一個新函數 csumEven : csumEven t @(Leaf i). =. let val x = csum t in if x mod 2 = 0 then x else 0. | csumEven t. = csum t. t1 = Leaf 2 t2 = Node (Leaf 4) (Leaf 5). 政 治 大 正確的計算 tree 中所有偶數值的總合。 立. csumEven 用來計算 Tree 中 Leaf 為偶數值的總合,然而實際執行 csumEven 函數並無法. csumEven t1 -- output 2.. ‧ 國. 學. csumEven t2 -- output 9 rather than 4... ‧. 因為當引數值為 Node 時,csumEven 會呼叫 csum 函數處理 Node 的部分,而 csum 函數. y. Nat. 接著遞迴執行 csum 而非 csumEven,所以導致 csumEven 的結果錯誤。針對遞迴函數的. er. io. sit. 程式碼重用問題,研究[15]提出使用函數式語言模擬物件導向語言的 dynamic dispatch, 將函數定義中使用到遞迴呼叫的部分保留先不決定,直到使用時才決定實際被遞迴呼叫. n. al. Ch. 為何。可擴充的遞迴程式如下 : sum this (Leaf i) = | sum this (Node (l, r)). i =. engchi. i n U. v. this l + this r. sumEven this t @(Leaf i) =. let val x = sum this t in if x mod 2 = 0 then x else 0. | sumEven this t. = sum this t. fsumEven t = sumEven fsumEven t fsumEven t2. -- output 4, it‟s expected value now. 17.

(26) 在程式中,可重用的遞迴函數多了一個額外的參數 this,所有預期在日後會被繼續重用 的遞迴函數皆必頇在函數一開始定義的時候就預留此 this 參數,但針對那些已存在的且 不可被重用的遞迴函數,便無法在不修改它的狀況下達到重用程式碼的目的。 由於遞迴函數 csum 不能重用的問題在於它每次遞迴呼叫時必非預期的對象 csumEven,若我們可在 csumEven 執行的期間動態改變 csum 遞迴呼叫的對象, 就可不修改 csum 定義而實現程式碼重用的目的。在剖面導向程式語言中我們常 會使用 cflow 來控制剖面模組影響的範圍,所以我們可以藉由 cflow 來修正. 政 治 大 even@advice around {csum+cflow(csumEven)} (arg) = case arg of { 立. csumEven 錯誤的結果. 學. ‧ 國. Leaf i -> csumEven arg;. _. -> proceed arg } in. -- output 9, advice even do not affect the call to csum. y. Nat. csum t2. ‧. csumEven t2 -- output correct value 4. er. io. sit. 此程式中,當 csumEven 執行時 advice even 會攔截所有 csum 函數呼叫,若引數 值為 Leaf 則改變函數呼叫的對象,而原先錯誤的 csumEven 也因為剖面模組的影. n. al. 響修正為正確的結果。. Ch. engchi. 18. i n U. v.

(27) 第四章 以 AspectFun 輔助實現泛型程式設計 此章節將介紹函數式語言上的泛型程式設計相關研究,並闡述在 AspectFun 上如何輔 助實現泛型程式設計的最初構想與遭遇問題,最後說明本研究為了以 AspectFun 輔助 實現泛型程式設計所進行的擴充及修改的實作細節。. 4.1 泛型程式設計. 政 治 大 4.1.1 重載函數(overloaded function) 立. 在函數式語言中,程式設計者除了可定義用來操作某特定型態的函數如 eval 函數 :. ‧ 國. 學. eval :: Expr -> Int. ‧. 也常會使用一些重載函數,所謂的重載函數是指該函數可用來操作程式中多種資料型態. y. Nat. 的集合,例如在函數式語言中經常被使用的 show、eq 函數,show 函數可用來將多種型. er. io. sit. 態的數值轉為字串方式呈現,eq 函數是用來比較兩個相同型態的數值是否相等。在 Haskell 語言中,程式設計者通常是使用 type class 機制來實現重載函數的定義與擴充,. n. al. i n Ch 針對該重載函數欲處理的新型態去定義函數如何處理。 engchi U. v. 重載函數的缺點是一旦程式擴充了新的型態,對於 eq 這類會用在程式中大多數型態的 函數,程式設計者就必頇隨著型態增加而擴充函數定義,若程式中有 n 個需求如同 eq 的函數,當程式新增了一種型態,我們便必頇進行 n 次擴充,這樣的程式設計方法雖然 可以解決程式需求,但在修改程式碼的過程中可能會造成新的問題,這種顯然不是個有 效率的程式設計方法。 在 Haskell 中提供了 deriving 機制可用來解決部分的重載函數擴充問題,若程式設 計者自行定義的資料型態欲被 show 函數操作,可在該資料型態定義加上 deriving Show,如 : data Tree a = Node (Tree a) (Tree a) | Leaf a deriving Show 19.

(28) 編譯器便會自動擴充 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 [] = “”. y. Nat. …(略). ++ pretty xs. ‧. pretty (x:xs) = pretty x. er. io. sit. pretty 函數目的是用來替換 show 函數簡略不容易閱讀的字串輸出,我們知道 pretty 並非 語言內建的重載函數,因此也就無法使用 deriving 機制解決函數擴充問題。. n. al. C 4.1.2 泛型程式設計與相關研究介紹. hengchi. i n U. v. 泛型程式設計是一種用來促進程式重用性的程式設計概念。泛型程式設計是指函數接收 一個額外的型態資訊,此型態資訊可以是該函數引數的型態,或是其回傳值的型態,函 數依據此型態資訊決定所要執行的運算為何,此函數又被稱為泛型函數。泛型函數所需 的型態資訊可視為是程式設計者在定義函數時隱含將此型態引數傳遞給函數或是明確 定義此型態引數的。 基於函數式語言的重載函數擴充問題,研究者提出了將泛型概念應用在函數式語 言,期望泛型函數的實現可以克服重載函數的重用性缺陷,函數在定義後不需重新修改 與擴充便可用來處理程式中所有已定義及尚未定義的資料型態。 在函數式語言的泛型程式設計相關研究中,Ralf Hinze 教授提出了一種在函數式語 20.

(29) 言上支援泛型程式設計的作法。它的作法主要概念如圖 4.1 所示,程式中額外使用一中 介資料型態 Spine a,而所有泛型函數只需定義如何去操作 Spine a 此中介資料型態,而 其他的資料型態如 Int、Char、List 等則藉由轉換函數 toSpine 被轉換為中介資料型態表 示,每當新增一種資料型態時,程式設計者只需要提供此新的資料型態如何轉換 Spine a 的轉換定義,即擴充重載函數 toSpine,而原來定義好的所有泛型函數不需要重新被修 改或擴充。. 立. 政 治 大. ‧. ‧ 國. 學. n. er. io. sit. y. Nat. al. Ch. engchi. i n U. v. 圖 4. 1 : Spine 泛型作法概念. 在 Hinze 教授作法中所提及的中介資料型態 Spine a,是將一般函數式語言的資料型 態宣告的結構抽象化,由於一般函數式語言的資料型態定義通常是 : data TypeName = ConstructorName arg1 arg2 arg3 arg4 … 每個資料建構函數都會有建構函數名稱以及建構函數所需的引數值,針對資料型態定義 在語法結構上的相似性因此可以被對應到 Spine a 表示,在 Spine a 中,資料建構函數名 稱以 Con 表示,若該建構函數需要引數,則使用 App 表示將建構函數 apply 至引數上, 而語言內建的基本資料型態(primitive type)如 Int、Char 則視為無引數的資料建構函數,. 21.

(30) 完整的 Spine a 的定義如下列所示 data Spine a = Con (Constr a) | forall b.ToSpine b => App (Spine (b -> a)) b data Constr a = Descr a 例如 Int 可被表示為 Con (Descr 0);Char 可被表示為 Con (Descr „a‟);而資料型態 List 根據其定義可知 List 被分為空串列(Nil)與非空串列(Cons a (List a)),因此 空串列可被表示為 Con (Descr [ ]),而非空串列可被表示為(App (App (Con (Descr (:) )). 政 治 大 在 Hinze 教授所提出的中介資料型態 Spine a 定義中,使用了 Haskell 語言的擴充 立. x) xs)。. Existential type,Existential type 的目的是為了將型態資訊隱藏起來,在一般的資. ‧ 國. 學. 料型態定義中,我們可以將建構函數所需的引數型態參數化 :. ‧. data Tree a = Node (Tree a) (Tree a) | Leaf a. y. Nat. 其中 a 是一型態變數,型態參數化的好處是我們不需針對各種型態的 Tree 作個別定義,. er. io. sit. 當建構函數被使用時,型態變數 a 便會依據引數型態而被綁定為某個特定型態,例如 (Leaf 5)我們可知此其型態為攜帶整數的 Tree。Existential type 是指一型態變數只. n. al. i n Ch 出現在資料型態定義的右邊(只在建構函數定義部分) engchi U. v. 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,在後續章節會 22.

(31) 說明 Existential type 對此研究造成的問題。. 4.2 實現泛型程式設計所面臨的問題 在 Hinze 教授提出的泛型方法中期望將所有的重載函數都可以泛型函數替代,然而此泛 型方法被發現並無法確保所有的泛型函數的計算結果都如同程式設計者預期,其中 strings 函數便是一個代表性問題指出此泛型作法的問題。strings 函數問題是指我們希望 設計一泛型函數 strings,它可用來蒐集其引數中所有被使用的字串值如. 政 治 大. strings “abc”. 立. expected output [“abc”]. ‧ 國. 學. strings 5.  expected output [ ]. ‧. strings [“abc”, “def”]. sit. y. Nat.  expected output [“abc”, “def”]. al. er. io. 實際執行的結果發現,當引數為一字串串列時,strings 函數的回傳值並非如同預期結果. v. n. 是其引數,而是一空串列。以下我們將分別以 type class 機制與 AspectFun 的剖面機制說 明其實作方式與問題點。. Ch. engchi. i n U. 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). 23.

(32) 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. sit. y. Nat. strings x = [x]. n. al. er. io. instance Strings a where strings x = strings‟ (toSpine x). Ch. engchi. i n U. v. strings‟ :: Spine a→[String] strings‟ (Con c) = [ ] strings‟ (App f x) = strings‟ f ++strings x 在上述程式片段中可見,當函數 strings 的引數不為字串型態時便將引數轉換為 Spine 表 示,然後交由輔助函數 strings‟遞迴處理,由於建構函數名稱不會產生任何字串值,因此 當引數為 Con 時直接回傳[ ];當引數為 App 時,我們已知 App 中的 f 是一 partially apply 的函數,其型態為 Spine (b -> a),因此將 f 遞迴交給 strings‟處理,x 是建構函數所需的 引數,其型態為某個未知型態 b,因此把 x 交由 strings 判斷此引數是否為 String 型態。. 24.

(33) 實際執行此程式時,當 strings 函數的引數為一個字串串列[“abc”, “def”]時,根據 toSpine 定義我們知道串列[“abc”, “def”]會先被轉換為(App (App (Con (Descr(:))) “abc”) [“def”]),由於在 strings‟的定義中會遞迴將 x 交由 strings 處理,即 List 的 Spine 編碼表 示中的”abc”與”def”字串值,然而在 strings 遞迴處理的過程中,並沒有正確呼叫 String -> [String]此型態的 strings 函數,卻是呼叫了 a -> [String]型態的 strings 函數,因此字串 值”abc”與”def”皆會被視為串列處理,造成 strings 輸出的結果不正確。 4-2-2 以 AspectFun 輔助實現泛型程式設計的實作與問題. 政 治 大 AspectFun 剖面機制去解決此問題,並認為以剖面實現的泛型方法相較於 type class 更為 立 基於在 Hasekll 中使用 type classes 輔助實現的泛型作法的錯誤結果,我們試圖使用. 直覺,並且可以避免函數擴充時的修改問題,然而 AspectFun 也發生了相同的結果錯誤. ‧ 國. 學. 問題 :. ‧. strings :: a -> [String] strings x = strings‟ (toSpine x) in. y. Nat. al. sit. n. strings‟ (App f x) = strings‟ f ++ strings x in. Ch. engchi. n@advice around {strings} (arg :: String) = [arg]. er. io. strings‟:: Spine a -> [String] strings‟ (Con c) = [ ]. i n U. v. in. 在上述程式片段中,我們以 type-scoped advice n 處理當引數型態為 String 時的例外工 作。在 AspectFun 中,我們期望每次 strings 被呼叫時皆會先觸發 advice n 判斷引數型態, 但實際執行的情況在 strings‟定義中呼叫 strings 時並沒有觸發 advice n,所以在 AspectFun 上也發生 type class 的問題。 歸納兩者的原因,我們發現造成問題的原因是由於 Spine a 定義中使用了 Existential type,由於 Haskell 與 AspectFun 皆是靜態型別的語言,在靜態型別語言中型態資訊只存 在於編譯過程中,程式設計者無法直接對型態資訊進行操作例如自行根據引數的型態資 訊判斷函數接下來所要執行的運算內容為何,只能依賴型態推導系統判斷。當程式中使. 25.

(34) 用 Existential type,型態推論系統為了確保型態安全只能推導出較抽象的型態資訊,因 此在 strings‟定義中呼叫 strings 時推導系統只能判斷出此時傳給 strings 的引數型態是某 個未知型態 a 而非具體的 String 型態,導致 advice n 不會被觸發,欲使 AspectFun 可以 輔助實現泛型程式設計,我們需要解決 Existential type 造成型態資訊不足問題,使得 advice n 可以被織入至 strings。. 4.3 以 AspectFun 輔助實現的泛型程式設計. 政 治 大 是將型態資訊保留至程式執行期間,讓程式設計者可以在執行期間自行根據型態選擇運 立 在 AspectFun 中,為了解決 Existential type 造成的型態資訊不足問題,我們採用的作法. n@advice around {strings} (arg) = String?  [arg]. ‧. ‧ 國. 學. 算工作,而非依賴編譯器藉著不足的型態資訊決定:. Others?  proceed arg. y. sit. n. a l5. er. io. 標籤如 :. Nat. 在靜態型別語言中保留型態資訊的作法在讓程式中所有的運算值都額外攜帶一個型態. Ch. Int. engchi. i n U. v. 在函數式語言中,我們可以很容易的藉由資料型態定義來編碼表示這類帶有型態標籤的 值 : data Typed a = Typed a (TypeRep a). data TypeRep a = Rint | RChar | forall b. RList (TypeRep b) 資料型態 Typed 用來表示一數值與其型態的配對,TypeRep 用來表示此型態標籤,例如 5  (Typed 5 RInt) „a‟ (Typed „a‟ RChar). 26.

(35) [1,2,3]  (Typed [1,2,3] (RList (RInt))) 使用 Typed 與 TypeRep 編碼方法,將型態資訊以值的方式保留下來,使得程式可以使用 這些型態資訊,我們便可以藉由這些保留下來的型態資訊,在程式執行期間自行依據型 態資訊判斷某個被推導為 Existential type 的值其確切型態為何,彌補靜態型別語言的不 足。 因應此型態保留方法的導入原來的 toSpine 與 strings 函數型態也必頇更改為 : strings :: Typed a -> [String]. 政 治 大 strings 與 toSpine 函數定義便可根據引數所攜帶的型態資訊分配的不同的運算工作上。 立 toSpine :: Typed a -> Spine a. 而原來的 Spine a 定義也必頇將 b 更改為 Typed b :. ‧ 國. 學. data Spine a = … | forall b. App (Spine (b->a)) (Typed b). ‧. 此外基於程式的型態安全問題,為了避免使用端將某個值與錯誤型態配對而成一 Typed. y. Nat. 值,如(Typed „a‟ RInt)即為一個配對錯誤範例,配對錯誤將會導致程式發生不可預期且. er. io. sit. 不易除錯的型態錯誤問題,為了避免此問題我們額外引用了 Equivalence types,對原有 的 TypeRep 定義也進行了部分修改 : :. n. al. Ch. data TypeRep a = RInt (Equiv a Int). engchi. i n U. v. |RChar (Equiv a Char). |forall b. Rlist (Rep b) (Equiv a [b]). data Equiv a b = Equiv (a -> b) (b -> a) 原來的 TypeRep 的建構函數都額外攜帶了一引數(Equiv …),Equiv a b 表示型態 a 與型 態 b 兩者是相等的,且 a 和 b 可以進行轉換,在使用端我們可用來判斷一 Typed 的值與 型態的配對是否正確,如 (Typed 1 (Rint equiv)) (Typed "ab" (Rlist (Rchar equiv) equiv)) 27.

(36) equiv = Equiv id id 在上述範例中,equiv 的型態是 Equiv a a,在 TypeRep 定義中限制了值的型態 a 必 頇和 Int 相等,因此在(Typed 1 (Rint equiv))限制了 Typed 的值的型態只能是 Int,若不為 Int 型態的值就是一個編譯期間的型態錯誤。 由於目前程式中所有的運算值都必頇以 Typed 將其編碼讓程式設計者自行處理型態 資訊,因此程式需要透過一額外方法辨認此 Typed 數值其實際型態為何,並將此 Typed 數值轉換為原先不攜帶型態資訊的數值以便程式操作,此工作即是使用 cast 此轉型函數. 政 治 大 若型態比對相同則為轉型成功,若型態比對不同則視為失敗 : 立. 處理,cast 函數會根據 Typed 所攜帶的型態資訊標籤與試圖轉換的目標型態進行比對,. cast :: Typed a -> Rep b -> Maybe (b, a -> b, b -> a). ‧ 國. 學 ‧. cast (Typed 1 (RInt equiv)) (RInt equiv). sit. y. Nat.  cast success, return (Just 1, ….). io. er. cast (Typed [1,2,3] (RList (RInt equiv) equiv) (Rlist (Rchar equiv) equiv)  cast fail, return Nothing. al. n. v i n Ch 針對 List 的資料型態,我們也透過一類似 cast 函數的 castList 函數判斷一引數是否實際 engchi U 為一串列 :. castList :: Typed a -> Maybe (IsList a) 此兩個轉型函數 cast 與 castList 在轉型失敗時皆會回傳 Nothing 表示,確保了程式在執 行期間的轉型失敗造成的型態錯誤。 在 4-2-2 節中我們已知 strings 的錯誤結果是由於 advice n 無法被觸發所造成,藉由 型態資訊的保留,程式已不需要藉由 type-scoped advice 去判斷引數型態,而是讓 advice n 在每一次 strings 被呼叫時直接觸發,並嘗試轉型判斷引數的型態是否為 String,若為 String 則回傳包含引數的串列,若轉型失敗,即引數型態不為 String,則交由原來的 strings 函數進行 Spine 轉換 : 28.

(37) n@advice around {strings} (arg) = case cast arg (Rlist (Rchar equiv) equiv) of { Just (b, _, _) -> [b] ; _. -> proceed arg } in. 而最後的實際執行結果可以發現,藉由型態程式化與轉型函數的方法,使得靜態型別語 言 AspectFun 得以克服型態資訊問題並輔助實現了泛型程式設計 : strings (Typed 1 (Rint equiv)) []. 政 治 大. strings (Typed "ab" (Rlist (Rchar equiv) equiv))  [ “ab” ]. 立. strings (Typed ["ab", "cd"] (Rlist (Rlist (Rchar equiv) equiv) equiv))). ‧ 國. 學.  [“ab”, “cd”]. ‧. 雖 然 AspectFun 和 Haskell 都是靜態型別語言而有型態資訊不足問題,套用. y. Nat. AspectFun 解決作法也可處理在 Haskell 上的問題,然而使用 AspectFun 的優勢在於剖面. er. io. sit. 語言的 proceed 機制所帶來的串連特性,假設未來我們需要對 strings 函數增加對 Ascii 的額外處理,在 AspectFun 中,我們只需要增加 advice 即可 :. al. n. v i n n2@advice around {strings}C(arg) = case cast arg U h e n g c h i (Rascii equiv) of { Just (b, _, _) -> …..(略) ;. _. -> proceed arg } in. 但是在 Haskell 中,使用 type class 並不允許 instance 針對某一相同的型態進行重覆的宣 告情況 : instance Strings (Typed a) where strings x = ….. -- for String. instance Strings (Typed a) where strings x = ……. -- duplicated, invalid. -- for Ascii. 若使用 type class 且欲對函數進行擴充時,程式設計者只能直接修改原有的 strings 29.

(38) 函數定義,修改舊有程式碼的行為會破壞程式的重用性與擴充性原則。 instance Strings (Typed a) where strings x = String?  do A Ascii?  do B. -- insert code here. Others?  do C. 4.4 相關的函數式剖面語言研究探討. 政 治 大 泛型作法。有別於 AspectFun 立自行定義 toSpine 函數,AspectML 提供了內建的(primitive) 在相關的函數式剖面語言研究中,AspectML[16]和本研究一樣採用了 Ralf Hinze 提出的. ‧ 國. 學. toSpine 函數讓使用者可將自定義的資料型態轉換為 Spine 表示,與 AspectFun 相同的是 兩者皆在程式執行期間對型態進行分析,AspectFun 藉由將型態資訊程式化保留型態資. ‧. 訊至執行期間處理,AspectML 藉由其語言的 typecase 機制在執行期間判斷引數型態。. sit. y. Nat. 和 AspectFun 不同的是 AspectML 的 toSpine 函數並不能被套用在語言內建的基本資料型. al. n. 然較具彈性。. er. io. 態(primitive type)上。對於設計泛型函數來說 ,函數可處理語言內建的基本資料型態顯. Ch. engchi. i n U. v. 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 中並未支 30.

(39) 援 Existential types 的資料型態宣告,因此我們首先必頇擴充 AspectFun 的資料型態宣 告。與 Haskell 不同的是,AspectFun 的型態推導系統中並不支援 type class 的推論,因 此在 AspectFun 的程式中使用 Existential types 的資料型態宣告時,無法將此型態變數加 上限制如 Show、ToSpine 等,在目前的 AspectFun 程式中我們僅提供最基礎的宣告方式 : forall type-variables . data-constructor Existential type 的使用雖然放寬了資料型態定義方式,允許程式設計者可以將型態資訊 隱藏,但是為了確保型態安全,一個被定義為 Existential types 型態的值不可以直接被函. 政 治 大 data Stack = forall e. Stk e Stack | Empty in 立. 數當作回傳值,如 :. push s e = Stk e s. 學. ‧ 國. -- push :: Stack -> e -> Stack in. ‧. -- pop :: Stack -> e. y. Nat. pop (Stk e s) = e in ….. er. io. sit. 在上述範例中,定義了一個使用 Existential types 的 Stack,我們可以藉由 push 放置任意 型態的值,但是 pop 就是一個不合法的使用方式,因為 e 是一個 Existential types,在型. al. n. v i n Ch 態推導過程中只能判斷出 e 是某個未知的型態,我們無法在確保型態安全的限制下去直 engchi U 接操作一個未知型態的值,為了判斷這一類不合法的使用,. 本研究實作 Existential types 的作法首先在 AspectFun 第一階段的型態推導過程中為所有 推導過程所使用的型態變數加上標籤,此標籤的目的在判斷此型態變數是否為 Existential types。在 AspectFun 的 parse 階段,一旦發現資料型態定義中使用了 forall 的 保留字,便會在此建構函數的型態資訊在被放入推導環境(Enviroment)前上加上對應的 標籤。 由於型態推導系統在推論完函數的型態後最後皆會呼叫 quantify 函數,quantify 函 數的作用是將系統推導出的型態資訊量化為 Type Scheme,如 : f :: v1 -> v1 31.

(40) quantify (v1 -> v1)  ∀a. a-> a 因此我們修改 quantify 函數使其在量化前先檢查是否存在有 Existential types 標籤的型態 變數,若發現存在有 Existential types 即為一不合法的使用,型態推導系統便中斷推導過 程,否則便繼續執行原來的 quantify 工作。 此外我們亦修改了 mgu 函數,mgu 函數作用在提取兩個型態間最高的置換集合(most general unification),確保型態推導系統所推導出的型態是最為抽象的,如 : mgu (v1-> v2) (v3 -> Int)  S = [(v1, v3), (v2, Int)]. 立. 政 治 大. 由於在提取 mgu 的過程中會經常會將一型態變數代換為其它的型態變數,若 v1 是某個. ‧ 國. 學. 被標示為 Existential types 的型態變數,但 v2 沒有標籤表示,將會導致 Existential types. ‧. 標籤資訊遺失 :. y. Nat. mgu (v1 (marked) -> v2) (v3 -> Int) S = [(v1 (marked), v3 ), (v2, Int)]. er. io. sit. 遺失標籤資訊. 為避免在置換的過程中遺失標籤資訊,若出現兩個型態變數要尋找最高置換集合時,帶. al. n. v i n Ch 有標籤的型態變數必頇傳遞他的標籤資訊給其它的型態變數 engchi U mgu (v1 (marked) -> v2) (v3 -> Int). S = [(v1 (marked), v3 (marked)), (v2, Int)]. 4.5.2 Predicate propagation 在先前的泛型程式議題中,我們使用 strings 此代表性問題來說明 Spine 泛型方法會造成 的錯誤結果,為使 AspectFun 可以實現 strings 函數,本研究也放寬了 advice 使用限制, 使得 mutual recursive 函數可以作為 advice 的 pointcut,但是也造成了 AspectFun 編譯器 所轉換出的程式碼錯誤,因此我們必頇在 AspectFun 型態推導系統額外執行 predicate propagation 以便修正程式碼錯誤問題。 在放寬 advice 對 mutual recursion 的使用限制後我們發現 mutual recursion functions 32.

(41) 會導致下列 AspectFun 程式轉換後的 Haskell 程式碼錯誤 : ng@advice around{g} (arg :: [Int]) = if null arg then [] else proceed arg in -- f :: [a] -> [a] f x = if null x then x else g x in -- g :: [a] -> [a] g x = f (tail x) in f [1, 2, 3]. 政 治 大 advice ng 型態為[Int] ->[Int],因此 g 會形成一個 predicate,且 f 會攜帶 predicate g (g :: [a] 立 在推導函數 f 時,由於 f 定義中使用到 g,然而目前只知 g 型態為[a]->[a],而函數 g 的. -> [a]),直到推導至程式主體 f [1,2,3]才確定 advice 可被織入,此時 f x = if null x then x. ‧ 國. 學. else g x 會被轉換為 f dg x = if null x then x else dg x,而 g 沒有攜帶任何 predicate 資訊因. ‧. 此不進行額外的轉換,此結果造成 g 定義中的呼叫 f 時引數數目錯誤,為了解決此問題,. sit. io. er. (tail x)。. y. Nat. 我們必頇將函數 f 的 predicate 資訊傳遞給 g,使得 g x = f (tail x)被轉換成 g dv x = f dv. 為了處理 mutual recursion 函數間 predicate 資訊的傳遞問題,本研究在 AspectFun. al. n. v i n C h propagation 處理步驟如下 的型態推導過程中時增加的 predicate : engchi U 1.. 蒐集最初的 predicate 資訊. ex : [ (f, [g]) ,. -- 函數 g 沒有 predicate. (g, []) ] 2.. -- 函數 f 帶有 predicate g. 建構程式中函數的相依關係圖 f. g g. 所有自定義的函數名稱為此相依關係圖的節點,advice 排除在此相依關係圖 外,若函數 f 的定義中使用 g,則形成一個 f  g 的邊,在每個邊上會帶有 predicate 資訊。 33.

(42) 3.. 依據最初的 predicate 資訊與函數相依關係找出程式中 mutual recursive 函數缺 少 predicate 的部分 f. g g. 4.. 進行 propagate f. g g. g. 政 治 大 4.5.3 Polymorphic mutual recursion 立. 在 strings 範例中,由於 strings 函數型態為 Typed a  [String],而 strings‟ 函數型態為. ‧ 國. 學. Spine a  [String],這兩函數皆為多型函數,當 strings 引數為一個整數串列 :. ‧. (Typed [1,2] (Rlist Rint …) :: Typed [Int]. sit. y. Nat. 我們可知此引數會被轉換為下列 Spine 編碼表示 :. io. er. App ((App (Con (Descr (:))) Typed1 Rint) (Typed [2] (RList RInt))) 當 strings‟第一次呼叫 strings 時,會將一型態為 Typed [Int]的值交由 strings 處理. n. al. App (Con (Descr (:))). Ch. (Typed1 Rint). engchi. i n U. v. 當 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‟傳遞兩 34.

(43) 次型態不同的引數值時,strings 型態始終保持為 Typed a -> [String],因此解決了多型遞 迴函數的型態錯誤。. 立. 政 治 大. ‧. ‧ 國. 學. n. er. io. sit. y. Nat. al. Ch. engchi. 35. i n U. v.

(44) 第五章 結論與未來研究 在此研究中,我們探討了一般函數式語言機制的在程式的可重用性與可擴充性議題 上的可行性。在 Expression Problem 議題中可見,剖面除了實現 type class 帶來的程 式重用性與擴充性,proceed 機制允許程式設計者針對已定義函數重複進行擴充,type class 在擴充函數時則會破壞程式碼的重用性原則。 此外我們並藉由遞迴函數的議題展示剖面機制對遞迴函數的重用性助益,以 cflow. 政 治 大. 此剖面機制動態改變程式的執行流程,修正遞迴函數因為定義關係造成函數不易重用的. 立. 問題,展現以剖面為基礎的程式建構方法另一優勢。. ‧ 國. 學. 在泛型程式設計的部分,延續了剖面在擴充函數上的助益,我們也利用型態程式化 的方法克服靜態型別語言在編譯期間的型態資訊不足問題,將型態資訊留至程式執行期. ‧. 間處理雖然會造成額外的程式設計與效能負擔,相對於其帶來的助益是合理的可接受. y. Nat. sit. 的。泛型程式設計的實現促進 AspectFun 的程式設計者可建構出重用性高的程式碼。. n. al. er. io. 在此論文的先前章節中,我們還有一個未探討的議題限制是個別編譯(separate. i n U. v. compilation),在 Expression Problem 議題中,解決 Expression Problem 的最終目標. Ch. engchi. 是希望舊有程式碼可以不用重新編譯。由於 AspectFun 是個靜態織入的語言,程式中定 義的剖面必頇在編譯過程中判斷是否會織入至函數中,當程式定義了新的剖面且此剖面 會影響到舊有定義的函數,在程式編譯的過程中需要知道舊有程式碼中函數的定義,編 譯器並且對 AspectFun 程式碼進行織入轉換,受限於 AspectFun 目前的型態推導系統與 編譯系統限制,目前的 AspectFun 還未能支援個別編譯,基於 Expression Problem 及 日漸龐大的應用程式系統,個別編譯有助於程式碼的重用性,未來我們期望修改 AspectFun 編譯器使其支援個別編譯。. 36.

(45) 參考文獻 [1] G. Kiczales, J. Lamping, A. Menhdhekar, C. Maeda, C. Lopes, J.-M. Loingtier, and J. 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. 政 治 大 [3] H. Masuhara, H. Tatsuzawa, 立 and A. Yonezawa, "Aspectual Caml: an aspect-oriented SIGPLAN international conference on Functional programming, 2005, pp. 306 – 319.. ‧ 國. 學. 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. sit. y. Nat. Aspect-Oriented Polymorphically Typed Functional Languages." Symposium of Static. al. er. io. Analysis (SAS), 2007.. v. n. [5] Mads Torgersen. “The Expression Problem Revisited – Four new solutions using generics.” ECOOP’04.. Ch. engchi. i n U. [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. 37.

(46) 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,. 政 治 大 Programming Systems Languages and Applications. Proceedings 立. [13] Gilad Bracha, William Cook. “Mixin-based inheritance.”, Conference on Object Oriented. on. object-oriented. programming. on. Object-oriented. programming. 學. ‧ 國. conference. of the European. systems ,languages, and applications, 1990, Page : 303 – 311.. ‧. [14] Andres Loh, Ralf Hinze, ”Open data types and open functions”, Proceedings of the 8th. sit. y. Nat. ACM SIGPLAN international conference on principle and practice of declarative. io. er. programming page, 2006, pp.133-144.. [15] James Cheney, Ralf Hinze, “A lightweight implementation of generics and dynamics.”,. al. n. v i n CSIGPLAN Proceeding of the 2002 ACM on Haskell U h e n gworkshop i h c. [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.. 38.

(47) 附錄 附錄一 使用 type class 企圖實現泛型方法的完整 Haskell 程式碼 (會造成 strings 錯誤結果) -- 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. sit. y. Nat. toSpine i = Con (Descr 0). io. er. instance ToSpine Char where toSpine c = Con (Descr „a‟). n. al. Ch. instance ToSpine a => ToSpine [a] where. engchi. i n U. 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). 39. v.

(48) strings‟ :: Spine a→[String] strings‟ (Con c) = [ ] strings‟ (App f x) = strings‟ f ++strings x. 立. 政 治 大. ‧. ‧ 國. 學. n. er. io. sit. y. Nat. al. Ch. engchi. 40. i n U. v.

(49) 附錄二 使用 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. in. n. er. io. al. sit. y. Nat. int@advice around {toSpine} (arg :: Int) = Con (Descr 0). i n U. v. char@advice around {toSpine} (arg :: Char) = Con (Descr „a‟). Ch. engchi. 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] (strings “teststr”, strings [“abc”, “def”]). 41. in.

(50) 附錄三 將型態資訊保留至程式執行期間的實現泛型方法的完整 AspectFun 程式碼 (strings 結果正確) --The poly-recursion patch is required to run this example data Equiv a b = Equiv (a -> b) (b -> a) in data Rep a = Rint (Equiv a Int) | Rchar (Equiv a Char) | forall b. Rlist (Rep b) (Equiv a [b]) in data Typed a = Typed a (Rep a) in data Constr a = Descr a in. 立. 政 治 大. ‧ 國. 學. data Spine a = Con (Constr a) | forall b. App (Spine (b->a)) (Typed b) in. ‧. data IsList a = forall b. IsList [b] (Rep a) (Rep b) (a -> [b]) ([b] -> a) in. Nat. n. al. er. io. sit. y. equiv :: Equiv a a equiv = Equiv id id in. eq_rev :: Equiv a b -> Equiv b a eq_rev (Equiv a2b b2a) = Equiv b2a a2b in. Ch. engchi. i n U. v. 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 42.

參考文獻

相關文件

在第一章我們已瞭解一元一次方程式的意義與解法,而在本章當中,我們將介紹

我們已經學過了一元一次方程式與二元一次方程式,在本章中,我們將學習一元二次

 MATLAB 程式使用 pass-by-value 的方 式,進行程式與函式間的溝通聯絡,當 程式呼叫函式時, MATLAB

使用 BibTEX 的 L A TEX 文件, 編譯過程有時有點讓人困惑。我們這裡假設以 foo.tex 為我們的 L A TEX 檔 (BibTEX 檔叫什麼無妨, 只要我們在文中引用

(1999) Resiliency in Action) ,我們 對組員在強化活動中有高 的期望,期望他們能為學校 作出貢獻,同時亦希望藉此

• 學生需理解《愛蓮 說》中「菊」的象 徵意義,也需通過 掌握《陋室銘》的 中心思想,理解劉 禹錫的人生態度。. •

微算機原理與應用 第6

我們衷心感謝各協作學校的參與,並在實施的過程中與我們進行專業交流,發展