咱們能夠經過自由數據結構(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的實現過程。