TOP

Scalaz(43)- 總結 :FP就是實用的編程模式(一)
2017-10-10 12:12:54 】 瀏覽:9067
Tags:

  完成了對Free Monad這部分內容的學習了解后,心頭豁然開朗,存在心里對FP的疑慮也一掃而光。之前也抱著跟大多數人一樣的主觀概念,認為FP只適合學術性探討、缺乏實際應用、運行效率低,很難發展成現實的軟件開發模式。Free Monad的出現恰恰解決我心中的疑問,更正了我對FP的偏見:Free Monad提供了一套在Monad 算法內(在 for-comprehension內)的行令編程(imperative programming)方法,解決了FP的復雜語法,使Monadic編程更貼近傳統編程模式的習慣和思維,程序意圖更容易理解。Free Monad的函數結構化(reification)有效解決了遞歸算法造成的堆棧溢出(stackoverflow)問題,使FP程序能夠安全運行,實現在現實中的應用。

  在學習scalaz初期,FP的類型和函數施用搞得我很無奈,不適應:FP類型的Functor,Applicative,Monad等等給我的印象是無比抽象的。而且接觸到的有關這些類型的具體使用例子又大多數是針對List,Option,Map這些教科書通用類型的,感覺FP就是一種對編程模式的學術探討,是用來改變思想的,沒什么實用價值。當然,FP的遞歸算法又更加深了我們對現實中選用它的疑慮。但從Free Monad反向回顧scalaz的這些基礎類型和函數,我好像漸漸地明白了它們在scalaz這個FP工具庫中存在的意義。回到我了解scalaz的目的:就是希望證實FP這種模式可以成為一種生產工具。之前已經了解了FP模式的優勢但對于它的實際應用還是存有疑慮。以我粗淺的標準來講,如果作為一種實際可用的編程語言,起碼必須具備以下幾點:

1、語法簡單,容易掌握

2、表達式簡潔、直白

3、能夠保證運行安全

試想我們如何能長期的編寫fa.flatMap(a => fb.flatMap(b => fc.map(...)))這樣的程序呢?FP針對泛函結構F[A]的運算有著一套全新的數據結構和函數施用方式,沒人能明白這樣的程序表達的到底是什么目的。這時我們遇到了flatMap函數的方法糖for-comprehension,它可用讓我們在一個for-loop里進行我們熟悉的行令式編程,就像下面這樣:

for { x <- getRecNo r <- getRecord(x) _ <- r.save() } yield ()

除去for-yield后不就是我們熟悉的編程方式嗎?我們已經習慣并掌握了這種編程方式。因為flatMap是Monad的運算函數,所以FP式的編程又被稱為Monadic Programming,直白來講就是用Monad來編程,或者就是在一個Monad殼子(context)里編程。可以說scalaz的所有東西最終都和Monad有關(everything is about Monad)。通過證明,任何Monad都必須是Functor和Applicative优乐棋牌app下载,所以在scalaz里提供的Functor,Applicative以及其它的基礎typeclass并不如我們想象的那樣好像沒什么實用價值,實際上scalaz是通過這些基礎typeclass為我們構建各種功能的Monad提供了支持的。現在看來這些基礎typeclass還是值得了解的。而且看來如果要進行FP編程,就必須先掌握Monad應用,因為我們需要把所有東西都升格成Monad。那么Monad真的像許多人感覺的那樣神秘、虛渺、觸不可及嗎?答案是否定的。接觸的多了我們就可以了解Monad的主要作用就是把一個算法,無論是一個值或者一個函數升格成Monad,這樣我們就可以在Monad-for-comprehension里使用它們了。看看scalaz里一些類型的Monad格式吧:

 

case class State (run: S => (A,S)) case class Reader(run: A => B) case class Writer(run: (W, A)) ...

 

它們都是把普通的函數或者運算包嵌在一個結構里然后在實現這個類型的flatMap函數時體現這些運算的具體意義。這些道理在scalaz的源代碼里都可以得到證實。所以我們根本不需要畏懼Monad,應該采取積極態度去充分了解掌握它。我印象中比較麻煩的是Monad轉換和功能結合,它們都涉及到類型匹配,需要較大的想象空間。

好了,有了Monad和各種功能轉換、集合方式,我們可以在for-comprehension里進行熟悉的編程了。那么會不會出現在一個for-loop里出現幾百行指令的情況呢?我認為不會,因為我們可以用函數組合方式把一個大程序分解成各種功能單一的簡單函數,然后逐層進行組合,最終的程序最多也就是十幾二十行。這種組合特性有賴于Free Monad提供的算式/算法關注分離(program/interpret separation of concern)模式。它可以把影響函數組合的副作用放到算法(interpret)階段,讓我們能夠在算式中實現程序間的組合。這個我用以下的代碼來示范一下:

val prgGetData = for { x <- getRecNo r <- getRecord(x) } yield r val prgUpdateRecord = for { x <- getData r <- prgGetData - <- r.updateAndSave() } yield () prgUpdateRecord.run(dbActions)

再有一個問題就是FP的運算方式了:我們可以看到運算一連串的flatMap是一種遞歸算法,除非使用尾遞歸算法优乐棋牌app下载,compiler是無法對算法進行優化的,那么運算flatMap就很容易會發生堆棧溢出錯誤(stackoverflow error),無法保障程序運行安全。Free Monad是通過函數結構化,既是把flatMap函數作為一種數據存放在heap內存上,然后通過折疊算法逐個運算,這和傳統的函數引用方式:即通過堆棧設置運算環境有根本不同,Free Monad是用heap換stack优乐棋牌app下载,避免了遞歸算法容易出現的堆棧溢出問題。這方面又解決了FP程序運行安全問題。

通過調研、演練后基本掌握了Monadic Programming(MP)的方式方法。現在把它總結如下:

MP編程可分三個環節:

1、編寫程序功能描述,是一串代數語法(AST)。不是即時程序(Programm)

2、把功能描述對應到具體的效果實現方式

3、最后,運算選定的實現方式

分成具體的步驟如下:

1、ADT:模擬語句,用F[A]類數據類型來模擬指令

 

object FreeADTs { trait Dialog[A] case class Ask(prompt: String) extends Dialog[String] case class Tell(msg: String) extends Dialog[Unit] implicit def dialogToFree[A](da: Dialog[A]) = Free.liftF(da) }

 

2、lift:把F[A]升格成Free Monad

 

  implicit def dia  
		

請關注公眾號獲取更多資料



首頁 上一頁 1 2 下一頁 尾頁 1/2/2
】【打印繁體】【】【】 【】【】【】 【關閉】 【返回頂部
上一篇scala學習手記4 - Java基本類型對.. 下一篇Windows和Linux(Ubuntu)下安裝S..