面向组合子程序设计方法 之 新约
生活随笔
收集整理的這篇文章主要介紹了
面向组合子程序设计方法 之 新约
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
每個小孩剛開始走路的時候都是跌跌撞撞的。?
我們不自量力,妄圖踩著上帝的步伐前進。結果就是這么幾個簡單的象白開水似的類。失望嗎?是不是造物試圖模仿造物主本身就是一種可笑的狂妄呢??
別急,讓我們失聲痛哭之前先看看我們這幾步走的是不是一錢不值。?
[list]?
1。logger可以把信息打印到log文件中。 ?
容易,直接創建一個WriterLogger就好了。?
2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進入error.log,什么進入warning.log,什么進入info.log也需要決定。 ?
不同的文件嗎?好辦啊。就是不同的PrintWriter對象了。?
Java代碼??Logger?err_log?=?writer(err_writer);;?? Logger?warning_log?=?writer(warning_writer);;?? Logger?info_log?=?writer(info_writer);;??
各個文件記錄不同重要程度的信息是么??
Java代碼??err_log?=?filter(ERROR,?err_log,?nop(););;?? warning_log?=?filter(WARNING,?warning_log,?nop(););;?? info_log?=?filter(INFO,?info_log,?nop(););;??
最終把三個不同的logger串聯起來就是了:?
Java代碼??Logger?logger?=?sequence(err_log,?warning_log,?info_log);;??
3。也許可以象ant一樣把所有的信息都打印到一個文件中。 ?
這就更簡單了,就一個是WriterLogger。?
4。每條logging信息是否要同時打印當前的系統時間?也是一個需要抉擇的問題。 ?
拿不定主意是么?沒關系,想好了再告訴我。?
反正,如果你需要系統時間,我只需要?
Java代碼??logger?=?timestamp(logger);;??
5。不僅僅是log文件,我們還希望能夠在標準錯誤輸出上直接看見錯誤,普通的信息可以打印到log文件中,對錯誤信息,我們希望log文件和標準輸出上都有。 ?
可以創建針對標準輸出的Logger,然后和打印log 文件的logger串聯起來。?
Java代碼??Logger?std_logger?=?writer(new?PrintWriter(System.err););;?? std_logger?=?ignore(ERROR,?std_logger);;?? logger?=?sequence(std_logger,?logger);;??
6。標準輸出上的東西只要通知我們出錯了就行,大概不需要詳細的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個簡短的exception的message就夠了。 ?
這里需要對std_logger稍微改寫一下:?
Java代碼??PrintWriter?out?=?new?PrintWriter(System.err);;?? std_logger?=?new?ErrorMessageLogger(out,?writer(out););;?? std_logger?=?ignore(ERROR,?std_logger,?nop(););;??
用ErrorMessageLogger來改寫對異常的log邏輯。?
7。warning似乎也應該輸出到屏幕上。 ?
好啊。就是把ignore函數里面的ERROR換成WARNING就好了?
Java代碼??std_logger?=?ignore(WARNING,?std_logger,?nop(););;??
8。不管文件里面是否要打印當前系統時間,屏幕上應該可以選擇不要打印時間。 ?
對std_logger不掉用timestamp就是了。?
9。客戶應該可以通過命令行來決定log文件的名字。 ?
這條和logger組合子其實沒什么關系。?
10。客戶可以通過命令行來決定log的細節程度,比如,我們只關心info一樣級別的信息,至于debug, verbose的信息,對不起,不要打印。 ?
生成那個最終使用的Logger對象的時候,再ignore一下就行了:?
Java代碼??logger?=?ignore(some_level,?logger,?nop(););;??
11。neptune生成的是一些Command對象,這些對象運行的時候如果出現exception,這些exception會帶有execution trace,這個execution trace可以告訴我們每個調用棧上的Command對象在原始的neptune文件中的位置(行號)。?
這種exception叫做NeptuneException,它有一個printExecutionTrace(PrintWriter)的方法來打印execution trace。?
所以,對應NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調用printExecutionTrace()。 ?
NeptuneExceptionLogger就是給這個準備的呀。?
12。neptune使用的是jaskell語言,如果jaskell腳本運行失敗,一個EvaluationException會被拋出,這個類有一個printEvaluationTrace(PrintWriter)的方法來打印evaluation trace,這個trace用來告訴我們每個jaskell的表達式在腳本文件中的位置。?
所以,對應EvaluationException,我們要在printStackTrace()之前,調用printEvaluationTrace()。 ?
JaskellExceptionLogger?
13。execution trace和evaluation trace應該被打印到屏幕上和log文件兩個地方。 ?
這就是說,上面兩個Logger應該被應用到std_logger和logger兩個對象中。?
14。因為printExecutionTrace()和printEvaluationTrace()本身已經打印了這個異常的getMessage(),所以,對這兩種異常,我們就不再象對待其它種類的異常那樣在屏幕上打印getMessage()了,以免重復。?
就是說,一旦一個exception被發現是NeptuneException,那么ErrorMessageLogger就要被跳過了。?
Java代碼??final?Logger?err_logger?=?new?ErrorMessageLogger(writer);;?? final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?err_logger);;?? final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?jaskell_logger);;?? return?neptune_logger;??
這個neptune_logger先判斷異常是不是NeptuneException,如果是,直接處理,否則,傳遞給jaskell_logger。jaskell_logger繼續判斷,如果不是它感興趣的,再傳遞給ErrorMessageLogger來做最后的缺省處理。?
15。也許還有一些暫時沒有想到的需求, 比如不是寫入log文件,而是畫個圖之類的變態要求 。?
放馬過來吧。看我們的組合子能不能對付。?
[/list:u]?
很驚訝地發現,就這么幾個小兒科似的積木,就似乎全部解決了曾讓我們煩惱的這些需求??
為了給大家一個完整的印象,下面是我實際項目中使用這些組合子應對上面這些需求的代碼:?
Java代碼??public?class?StreamLogger?{?? ??private?final?OutputStream?out;?? ???? ??/**? ???*?To?create?a?StreamLogger?object.? ???*?@param?out?the?OutputStream?object?that?the?log?message?should?go?to.? ???*/?? ??public?StreamLogger(OutputStream?out);?{?? ????this.out?=?out;?? ??}?? ???? ??/**? ???*?To?get?the?OutputStream?that?the?log?messages?should?go?to.? ???*/?? ??public?OutputStream?getStream();?{?? ????return?out;?? ??}?? ??private?static?Logger?getBaseLogger(PrintWriter?writer);{?? ????final?Logger?nop?=?new?NopLogger();;?? ????final?Logger?base?=?Loggers.logger(writer);;?? ????final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?nop);;?? ????final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?nop);;?? ????return?Loggers.sequence(?? ????????new?Logger[]{neptune_logger,?jaskell_logger,?base}?? ????);;?? ??}?? ??private?static?Logger?getEchoLogger(PrintWriter?writer);{?? ????return?new?ErrorMessageLogger(writer);;?? ??}?? ??private?static?Logger?getErrorLogger(PrintWriter?writer);{?? ????final?Logger?err_logger?=?new?ErrorMessageLogger(writer);;?? ????final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?err_logger);;?? ????final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?jaskell_logger);;?? ????return?neptune_logger;?? ??}?? ??/**? ???*?Get?the?Logger?instance.? ???*?@param?min_level?the?minimal?critical?level?for?a?log?message?to?show?up?in?the?log.? ???*?@return?the?Logger?instance.? ???*/?? ??public?Logger?getDefaultLogger(int?min_level);{?? ????final?PrintWriter?writer?=?new?PrintWriter(out,?true);;?? ????final?PrintWriter?err?=?new?PrintWriter(System.err,?true);;?? ????final?PrintWriter?warn?=?new?PrintWriter(System.out,?true);;?? ????final?Logger?all?=?Loggers.sequence(new?Logger[]{?? ????????Loggers.ignore(getErrorLogger(err);,?Logger.ERROR);,?? ????????Loggers.filter(getEchoLogger(warn);,?Logger.WARNING);,?? ????????getBaseLogger(writer);?? ??????}?? ????);;?? ????return?Loggers.ignore(all,?min_level);;?? ??}?? }??
為了偷懶,我沒有用配置文件,就是把這些策略硬編碼進java了。好在上面的代碼非常declarative,改起來也很容易。?
沒習慣讀代碼的朋友。這里奉勸還是讀一讀吧。很多時候,代碼才是說明問題的最好手段。我相信,只有讀了代碼,你才能真正嘗到CO的味道。?
有朋友問,你這個東西和decorator pattern有什么區別呀?乍看上去,還真是長得差不多呢。不都是往現有的某個對象上面附加一些功能嗎??
也許是把。我不知道象SequenceLogger這種接受一個數組的,是否也叫做對數組的decorator;也不知道IgnoreLogger接受了兩個Logger對象,這個decorator究竟是修飾誰的呢??
其實,叫什么名字無所謂。我這里要講的,是一種從基本粒子推演組合的思路。形式上它也許碰巧象decorator, 象ioc。但是正如workinghard所說(這句話深得我心),思路的切入點不同。?
如果你仔細看上面的代碼,也許你會有所感覺:對Logger的千奇百怪的組合本身已經有點象一個程序代碼了。?
如果用偽碼表示:?
Java代碼??all_logger?=?ignore?messages?below?ERROR?for?getErrorLogger(err);;?? ????????filter?messages?except?WARNING?for?getEchoLogger(warn);;?? ????????baseBaseLogger(writer);;?? ignore?messages?below?lvl?for?all_logger;??
當組合子越來越多,需求越來越復雜,這個組合就會越來越象個程序。?
這里,實際上,(至少我希望如此),我們的思維已經從打印什么什么東西上升為在Logger這個級別的組裝了。?
這也就是所謂higher order logic的意思。?
所謂combinator-oriented,在這里,就體現為系統性地在高階邏輯的層面上考慮問題,而不是如decorator那樣的零敲碎打的幾個功能模塊。?
大量的需求邏輯被以聲明式的方式在高階邏輯中實現,而基本的組合子只負責實現原字操作。?
當然,缺點也是明顯的,對此我不諱言:?
[list]高階邏輯不容易調試,當我們使用一個組合了十幾層的復雜的Logger對象的時候(真正用了co這種情況不少見),一旦出現bug,跟蹤的時候我們就會發現象是陷入了一個迷宮,從一個組合子跟蹤進入另一個組合子,繞來繞去。?
另外,異常的stack trace也無法反映組合層次關系,造成錯誤定位麻煩。[/list:u]?
這也許不是co本身的問題,而是因為java這種oo語言對co沒有提供語言上的支持。但是無論如何,這對在java中大規模使用co造成了障礙。?
也許你還無法理解。平時我們在java中用那么幾個decorator,本身非常簡單,所以debug, trace都不是問題。可是,一旦oriented起來,情況就不同了。街上有兩輛車和成千上萬輛車,對交通造成的壓力截然不同。?
還有朋友,對co如何把握有疑問。難道co就是瞎貓碰死耗子么??
其實,無論co還是oo,對設計者都是有一定要求的。?
oo要求你了解需求,并且經驗豐富,也要有一點點運氣。?
co也要求經驗,這個經驗是設計組合子系統的經驗。什么樣的組合子是好的?怎么才算是足夠簡單?什么組合規則是合理的?等等,這些,也有規律可循,就像oo的各種模式一樣。同時,也可以refactor。畢竟,怎么簡單地想問題比怎么分解復雜問題可能還是要容易掌握一點。?
不過,co對經驗的要求稍微小一點,但是對數學和邏輯的基本功要求則多了一點。有了一些數學和邏輯方面的基本功,那么設計組合子就會輕松的多。?
co也要有一點點運氣。所以遇到你怎么想也想不明白的情況,就別死抗啦,也許這個問題就抽象不出組合子來,或者以我們普通人的智慧抽象不出來。?
co是銀彈嗎?當然不是,至少以我的功力沒有這種自信。?
遇到復雜的問題我也是先分解需求,面向接口的。只有問題的規模被控制在了一定的范圍,我才會試圖用co來解決問題。靠著對co的一些經驗和感覺,一旦發現了可以組合子化的概念,成果會非常顯著。?
而且,co和oo關注的層面也不同。co是針對一個單獨的概念(這點倒有點象ao),一點一點演繹,構成一個可以任意復雜的系統,一個好的co也會大大減少需要引入的概念數。而oo是處理大量互相或者有聯系,或者沒有聯系的概念,研究怎么樣把一個看上去復雜的系統的復雜度控制住。所以兩者并不是互相排斥的。自頂向下,自底向上,也許還是兩手一起抓更好些。?
這段時間應用co做了幾個軟件后,感覺co最擅長的領域是:?
問題域有比較少的概念,概念簡明清晰(比如logger, predicate, factory,command),但是對概念的實現要求非常靈活的場合。?
這種時候,用oo就有無處下嘴之感。畢竟概念已經分解了,職責也清楚,就是怎么提供一個可以靈活適應變化的體系,而對這個,oo并沒有提供一個方法論。基本上就是用po的方法吭哧吭哧硬做。而co此時就可以大展用武之地。彌補這個空隙。?
看過圣經的,也許有感覺,舊約里面的上帝嚴厲,經典,就像是一個純粹的fp語言,每個程序員都被迫按照函數式,組合子的方式思考問題,你感覺困難?那是你不夠虔誠。你們人不合我的心意,我淹死你們!?
而新約里面,明顯添加了很多人情味兒。上帝通過自己的兒子和人們和解了。既然淹死一波,再來一波還是這樣,那么是不是說大家應該各讓一步呢??
co和oo,既然各自都不能宣稱自己就是銀彈,那么為什么不能拉起手來呢?我們不是神,不可能真正按照神的方式用基本粒子組合演化世界,所以就別象清教徒一樣苦苦追求不可能的目標了。但是,上帝的組合之道畢竟相當巧妙,在有些時候荊棘里面出現火焰的時候,我們又何必固執地拒絕造物主的好意呢?禮貌地說一聲:“謝了!老大”。不是皆大歡喜??
這一節我們繼續講解了這個logging的例子。實際上,logging是一個非常簡單的組合子,下面如果大家對這個例子有疑問,我會盡力解答。?
我們不自量力,妄圖踩著上帝的步伐前進。結果就是這么幾個簡單的象白開水似的類。失望嗎?是不是造物試圖模仿造物主本身就是一種可笑的狂妄呢??
別急,讓我們失聲痛哭之前先看看我們這幾步走的是不是一錢不值。?
[list]?
1。logger可以把信息打印到log文件中。 ?
容易,直接創建一個WriterLogger就好了。?
2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進入error.log,什么進入warning.log,什么進入info.log也需要決定。 ?
不同的文件嗎?好辦啊。就是不同的PrintWriter對象了。?
Java代碼??
各個文件記錄不同重要程度的信息是么??
Java代碼??
最終把三個不同的logger串聯起來就是了:?
Java代碼??
3。也許可以象ant一樣把所有的信息都打印到一個文件中。 ?
這就更簡單了,就一個是WriterLogger。?
4。每條logging信息是否要同時打印當前的系統時間?也是一個需要抉擇的問題。 ?
拿不定主意是么?沒關系,想好了再告訴我。?
反正,如果你需要系統時間,我只需要?
Java代碼??
5。不僅僅是log文件,我們還希望能夠在標準錯誤輸出上直接看見錯誤,普通的信息可以打印到log文件中,對錯誤信息,我們希望log文件和標準輸出上都有。 ?
可以創建針對標準輸出的Logger,然后和打印log 文件的logger串聯起來。?
Java代碼??
6。標準輸出上的東西只要通知我們出錯了就行,大概不需要詳細的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個簡短的exception的message就夠了。 ?
這里需要對std_logger稍微改寫一下:?
Java代碼??
用ErrorMessageLogger來改寫對異常的log邏輯。?
7。warning似乎也應該輸出到屏幕上。 ?
好啊。就是把ignore函數里面的ERROR換成WARNING就好了?
Java代碼??
8。不管文件里面是否要打印當前系統時間,屏幕上應該可以選擇不要打印時間。 ?
對std_logger不掉用timestamp就是了。?
9。客戶應該可以通過命令行來決定log文件的名字。 ?
這條和logger組合子其實沒什么關系。?
10。客戶可以通過命令行來決定log的細節程度,比如,我們只關心info一樣級別的信息,至于debug, verbose的信息,對不起,不要打印。 ?
生成那個最終使用的Logger對象的時候,再ignore一下就行了:?
Java代碼??
11。neptune生成的是一些Command對象,這些對象運行的時候如果出現exception,這些exception會帶有execution trace,這個execution trace可以告訴我們每個調用棧上的Command對象在原始的neptune文件中的位置(行號)。?
這種exception叫做NeptuneException,它有一個printExecutionTrace(PrintWriter)的方法來打印execution trace。?
所以,對應NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調用printExecutionTrace()。 ?
NeptuneExceptionLogger就是給這個準備的呀。?
12。neptune使用的是jaskell語言,如果jaskell腳本運行失敗,一個EvaluationException會被拋出,這個類有一個printEvaluationTrace(PrintWriter)的方法來打印evaluation trace,這個trace用來告訴我們每個jaskell的表達式在腳本文件中的位置。?
所以,對應EvaluationException,我們要在printStackTrace()之前,調用printEvaluationTrace()。 ?
JaskellExceptionLogger?
13。execution trace和evaluation trace應該被打印到屏幕上和log文件兩個地方。 ?
這就是說,上面兩個Logger應該被應用到std_logger和logger兩個對象中。?
14。因為printExecutionTrace()和printEvaluationTrace()本身已經打印了這個異常的getMessage(),所以,對這兩種異常,我們就不再象對待其它種類的異常那樣在屏幕上打印getMessage()了,以免重復。?
就是說,一旦一個exception被發現是NeptuneException,那么ErrorMessageLogger就要被跳過了。?
Java代碼??
這個neptune_logger先判斷異常是不是NeptuneException,如果是,直接處理,否則,傳遞給jaskell_logger。jaskell_logger繼續判斷,如果不是它感興趣的,再傳遞給ErrorMessageLogger來做最后的缺省處理。?
15。也許還有一些暫時沒有想到的需求, 比如不是寫入log文件,而是畫個圖之類的變態要求 。?
放馬過來吧。看我們的組合子能不能對付。?
[/list:u]?
很驚訝地發現,就這么幾個小兒科似的積木,就似乎全部解決了曾讓我們煩惱的這些需求??
為了給大家一個完整的印象,下面是我實際項目中使用這些組合子應對上面這些需求的代碼:?
Java代碼??
為了偷懶,我沒有用配置文件,就是把這些策略硬編碼進java了。好在上面的代碼非常declarative,改起來也很容易。?
沒習慣讀代碼的朋友。這里奉勸還是讀一讀吧。很多時候,代碼才是說明問題的最好手段。我相信,只有讀了代碼,你才能真正嘗到CO的味道。?
有朋友問,你這個東西和decorator pattern有什么區別呀?乍看上去,還真是長得差不多呢。不都是往現有的某個對象上面附加一些功能嗎??
也許是把。我不知道象SequenceLogger這種接受一個數組的,是否也叫做對數組的decorator;也不知道IgnoreLogger接受了兩個Logger對象,這個decorator究竟是修飾誰的呢??
其實,叫什么名字無所謂。我這里要講的,是一種從基本粒子推演組合的思路。形式上它也許碰巧象decorator, 象ioc。但是正如workinghard所說(這句話深得我心),思路的切入點不同。?
如果你仔細看上面的代碼,也許你會有所感覺:對Logger的千奇百怪的組合本身已經有點象一個程序代碼了。?
如果用偽碼表示:?
Java代碼??
當組合子越來越多,需求越來越復雜,這個組合就會越來越象個程序。?
這里,實際上,(至少我希望如此),我們的思維已經從打印什么什么東西上升為在Logger這個級別的組裝了。?
這也就是所謂higher order logic的意思。?
所謂combinator-oriented,在這里,就體現為系統性地在高階邏輯的層面上考慮問題,而不是如decorator那樣的零敲碎打的幾個功能模塊。?
大量的需求邏輯被以聲明式的方式在高階邏輯中實現,而基本的組合子只負責實現原字操作。?
當然,缺點也是明顯的,對此我不諱言:?
[list]高階邏輯不容易調試,當我們使用一個組合了十幾層的復雜的Logger對象的時候(真正用了co這種情況不少見),一旦出現bug,跟蹤的時候我們就會發現象是陷入了一個迷宮,從一個組合子跟蹤進入另一個組合子,繞來繞去。?
另外,異常的stack trace也無法反映組合層次關系,造成錯誤定位麻煩。[/list:u]?
這也許不是co本身的問題,而是因為java這種oo語言對co沒有提供語言上的支持。但是無論如何,這對在java中大規模使用co造成了障礙。?
也許你還無法理解。平時我們在java中用那么幾個decorator,本身非常簡單,所以debug, trace都不是問題。可是,一旦oriented起來,情況就不同了。街上有兩輛車和成千上萬輛車,對交通造成的壓力截然不同。?
還有朋友,對co如何把握有疑問。難道co就是瞎貓碰死耗子么??
其實,無論co還是oo,對設計者都是有一定要求的。?
oo要求你了解需求,并且經驗豐富,也要有一點點運氣。?
co也要求經驗,這個經驗是設計組合子系統的經驗。什么樣的組合子是好的?怎么才算是足夠簡單?什么組合規則是合理的?等等,這些,也有規律可循,就像oo的各種模式一樣。同時,也可以refactor。畢竟,怎么簡單地想問題比怎么分解復雜問題可能還是要容易掌握一點。?
不過,co對經驗的要求稍微小一點,但是對數學和邏輯的基本功要求則多了一點。有了一些數學和邏輯方面的基本功,那么設計組合子就會輕松的多。?
co也要有一點點運氣。所以遇到你怎么想也想不明白的情況,就別死抗啦,也許這個問題就抽象不出組合子來,或者以我們普通人的智慧抽象不出來。?
co是銀彈嗎?當然不是,至少以我的功力沒有這種自信。?
遇到復雜的問題我也是先分解需求,面向接口的。只有問題的規模被控制在了一定的范圍,我才會試圖用co來解決問題。靠著對co的一些經驗和感覺,一旦發現了可以組合子化的概念,成果會非常顯著。?
而且,co和oo關注的層面也不同。co是針對一個單獨的概念(這點倒有點象ao),一點一點演繹,構成一個可以任意復雜的系統,一個好的co也會大大減少需要引入的概念數。而oo是處理大量互相或者有聯系,或者沒有聯系的概念,研究怎么樣把一個看上去復雜的系統的復雜度控制住。所以兩者并不是互相排斥的。自頂向下,自底向上,也許還是兩手一起抓更好些。?
這段時間應用co做了幾個軟件后,感覺co最擅長的領域是:?
問題域有比較少的概念,概念簡明清晰(比如logger, predicate, factory,command),但是對概念的實現要求非常靈活的場合。?
這種時候,用oo就有無處下嘴之感。畢竟概念已經分解了,職責也清楚,就是怎么提供一個可以靈活適應變化的體系,而對這個,oo并沒有提供一個方法論。基本上就是用po的方法吭哧吭哧硬做。而co此時就可以大展用武之地。彌補這個空隙。?
看過圣經的,也許有感覺,舊約里面的上帝嚴厲,經典,就像是一個純粹的fp語言,每個程序員都被迫按照函數式,組合子的方式思考問題,你感覺困難?那是你不夠虔誠。你們人不合我的心意,我淹死你們!?
而新約里面,明顯添加了很多人情味兒。上帝通過自己的兒子和人們和解了。既然淹死一波,再來一波還是這樣,那么是不是說大家應該各讓一步呢??
co和oo,既然各自都不能宣稱自己就是銀彈,那么為什么不能拉起手來呢?我們不是神,不可能真正按照神的方式用基本粒子組合演化世界,所以就別象清教徒一樣苦苦追求不可能的目標了。但是,上帝的組合之道畢竟相當巧妙,在有些時候荊棘里面出現火焰的時候,我們又何必固執地拒絕造物主的好意呢?禮貌地說一聲:“謝了!老大”。不是皆大歡喜??
這一節我們繼續講解了這個logging的例子。實際上,logging是一個非常簡單的組合子,下面如果大家對這個例子有疑問,我會盡力解答。?
然后,我們會進軍下一個例子。
from:http://ajoo.iteye.com/blog/23307
總結
以上是生活随笔為你收集整理的面向组合子程序设计方法 之 新约的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论面向组合子程序设计方法 之 燃烧的荆棘
- 下一篇: 论面向组合子程序设计方法 之 oracl