Scalaz(31)- Free :自由數據結構-算式和算法的關注分離

   咱們能夠經過自由數據結構(Free Structure)實現對程序的算式和算法分離關注(separation of concern)。算式(Abstract Syntax Tree, AST)即運算表達式,是對程序功能的描述。算法則是程序的具體運算方式(Interpreter),它賦予了算式意義。下面咱們先用一個例子簡單解釋何爲算式、算法:算法

用一個簡單的表達式 1+2+3,這個表達式同時包含了算式和算法:運算表達式是 a Op b Op c, 算法是:Int加法,a,b,c爲Int, oP爲Int+。那麼咱們可不可把它分解成算式和算法呢?咱們能夠先把算式推導出來:Op(a,Op(b,c))。咱們能夠在算法裏對Op即a,b,c進行多種定義,即經過這些定義咱們能賦予算式不一樣的意義。這個例子能夠形象的描述算式、算法關注分離的全過程:抽象描述咱們要運算的程序,定義具體運算方式能夠分開進行。數組

實際上 1+2+3能夠說是一種Monoid操做。咱們看看是否能從中推導出Free Monoid,一個Monoid自由數據結構用來實現Monoidal操做的算式、算法分離關注。針對任意基本類型A的Monoid定義以下:數據結構

一、一個二元函數 append: (A,A)=>Aapp

二、一個A類型的初始值(零值)zeroide

Monoid必須遵循如下定律:函數

一、append函數的關聯性associativity: 對任意A類型的x,y,z - append(x,append(y,z)) === append(append(x,y),z)this

二、zero的同一概identity law: 對任意類型的x - append(zero,x) === append(x,zero)spa

根據以上定律,上面的表達式 1+2+3 === 1+(2+(3+0))。它的算式能夠是這樣:append(x,append(y,append(z,zero)))。那麼咱們應該能夠獲得這樣的Free Monoid自由數據結構:scala

1 sealed trait FreeMonoid[+A] 2 final case object Zero extends FreeMonoid[Nothing] 3 final case class Append[A](l: A, r: FreeMonoid[A]) extends FreeMonoid[A]

1::2::3::Nil >>> List(1,2,3),若是A是個Monoid那麼List[A]也是個Monoid,List[A]是個Free Monoid自由數據結構,咱們看下面的示範:code

1 def listOp[A](l: List[A]): FreeMonoid[A] = l match { 2     case Nil => Zero 3     case h :: t => Append(h,listOp(t)) 4 }                                                 //> listOp: [A](l: List[A])Exercises.freestruct.FreeMonoid[A]
5 listOp(List(1,2,3))                               //> res0: Exercises.freestruct.FreeMonoid[Int] = Append(1,Append(2,Append(3,Zero 6                                                   //| )))

List是一個Free Monoid, 它的 Nil === Zero,  a ++ b === Append(a,b)。

一樣,咱們能夠從Monad的特性操做函數來推導Free Monad自由數據結構。咱們能夠用如下操做函數來構建一個Monad M[_]:

一、point: A => M[A]

二、join: M[M[A]] => M[A]

三、map: (M[A], A => B) => M[B]

(point+flatMap組合一樣能構建Monad)

Free Monad是基於類型構建器Functor F[_]的Free Monoid, 因此Free Monad的定義應該是這樣的:

sealed trait Free[F[_],A]

咱們能夠直接把point轉換成case class:

final case class Return[F[_],A](a: A) extends Free[F,A] 

join的輸入類型是F[F[A]],咱們須要把Free[F,A]放在內裏:

final case class Suspend[F[_],A](ffa: F[Free[F,A]) extends Free[F,A]

咱們如今能夠猜想Free Monad的自由數據結構定義以下:

1 sealed trait Free[F[_], A] 2 final case class Return[F[_],A](a: A) extends Free[F,A] 3 final case class Suspend[F[_],A](ffa: F[Free[F,A]]) extends Free[F,A]

咱們只需證實用以上結構能夠實現Monad的全部特性操做函數,那麼這個Free就是一個用Functor F產生Monad的Monad構造器,一個最簡單結構的Monad構造器,即Free Monad:

 1 import scalaz.Functor  2 final case class Return[F[_],A](a: A) extends Free[F,A]  3 final case class Suspend[F[_],A](ffa: F[Free[F,A]]) extends Free[F,A]  4 sealed trait Free[F[_],A] {  5   def point(a: A) = Return[F,A](a)  6   def flatMap[B](f: A => Free[F,B])(implicit F: Functor[F]): Free[F,B] =
 7     this match {  8       case Return(a) => f(a)  9       case Suspend(ffa) => Suspend[F,B](F.map(ffa)(fa => fa flatMap f)) 10  } 11   def map[B](f: A => B): Free[F,B] = flatMap(a => Return[F,B](f(a))) 12   def join(ffa: F[Free[F,A]]): Free[F,A] = Suspend[F,A](ffa) 13 
14 }

這個Free自由數據結構足夠支持咱們實現point,flatMap,map,join這幾個Monad特性操做函數,因此Free是個Free Monad。

若是Free是個Free Monad,咱們能夠把Free[F,A]裏的F[A]當作Program[Commands]。即咱們能夠用命令集Commands來獨立描述程序Program。最終的程序Program是不會產生反作用的,因此允許最大限度的函數組合(function composition)。對Program的具體運算方法則能夠獨立分開實現。咱們將在下次討論中着重介紹Free Monad的實際應用方式:AST和Interpreter的實現過程。

相關文章
相關標籤/搜索