在前面的討論里我們提到自由數據結構就是產生某種類型的最簡化結構,比如:free monoid, free monad, free category等等。我們也證明瞭List[A]是個free monoid。我們再看看free monad結構Free的定義:scalaz/Free.scala 我們在上
在前面的討論里我們提到自由數據結構就是產生某種類型的最簡化結構,比如:free monoid, free monad, free category等等。我們也證明瞭List[A]是個free monoid。我們再看看free monad結構Free的定義:scalaz/Free.scala
/** A free operational monad for some functor `S`. Binding is done using the heap instead of the stack,
* allowing tail-call elimination. */
sealed abstract class Free[S[_], A] {
...
/** Return from the computation with the given value. */
private[scalaz] case class Return[S[_], A](a: A) extends Free[S, A]
/** Suspend the computation with the given suspension. */
private[scalaz] case class Suspend[S[_], A](a: S[Free[S, A]]) extends Free[S, A]
...
我們在上一篇里證明過Free就是free monad,因為Free是個Monad而且它的結構是最簡單的了:
1、Free[S[_],A]可以代表一個運算
2、case class Return是一個數據結構。Return(a:A)代表運算結束,運算結果a存放在結構中。另一個意義就是Monad.point(a:A),把一個任意A值a升格成Free
3、case class Suspend也是另一個數據結構。Suspend(a: S[Free[S,A]])代表把下一個運算存放在結構中。如果用Monad.join(a: F[F[A]])表示,那麼裡面的F[A]應該是個Free[S,A],這樣我們才可能把運算結束結構Return[S,A](a:A)存放到Suspend中來代表下一步結束運算。
註意,Suspend(a: S[Free[S,A]])是個遞歸類型,S必須是個Functor,但不是任何Functor,而是map over Free[S,A]的Functor,也就是運算另一個Free[S,A]值。如果這個Free是Return則返回運算結果,如果是Suspend則繼續遞歸運算。
簡單來說Free是一個把Functor S[_]升格成Monad的產生器。我們可以用Free.liftF函數來把任何一個Functor升格成Monad。看看Free.liftF的函數款式就知道了:
/** Suspends a value within a functor in a single step. Monadic unit for a higher-order monad. */
def liftF[S[_], A](value: => S[A])(implicit S: Functor[S]): Free[S, A] =
Suspend(S.map(value)(Return[S, A]))
liftF可以把一個S[A]升格成Free[S,A]。我們用個例子來證明:
1 package Exercises
2 import scalaz._
3 import Scalaz._
4 import scala.language.higherKinds
5 import scala.language.implicitConversions
6 object freelift {
7 trait Config[+A] {
8 def get: A
9 }
10 object Config {
11 def apply[A](a: A): Config[A] = new Config[A] { def get = a}
12 implicit val configFunctor = new Functor[Config] {
13 def map[A,B](ca: Config[A])(f: A => B) = Config[B](f(ca.get))
14 }
15 }
16
17 val freeConfig = Free.liftF(Config("hi config")) //> freeConfig : scalaz.Free[Exercises.freelift.Config,String] = Suspend(Exercises.freelift$Config$$anon$2@d70c109)
在上面的例子里Config是個運算A值的Functor。我們可以用Free.liftF把Config(String)升格成Free[Config,String]。實際上我們可以把這個必須是Functor的門檻取消,因為用Coyoneda就可以把任何F[A]拆解成Coyoneda[F,A],而Coyoneda天生是個Functor。我們先看個無法實現map函數的F[A]:
1 trait Interact[+A] //Console交互
2 //println(prompt: String) then readLine 返回String
3 case class Ask(prompt: String) extends Interact[String]
4 //println(msg: String) 不反回任何值
5 case class Tell(msg: String) extends Interact[Unit]
由於Ask和Tell都不會返回泛類值,所以無需或者無法實現map函數,Interact[A]是個不是Functor的高階類。我們必須把它轉成Coyoneda提供給Free產生Monad:
1 Free.liftFC(Tell("hello")) //> res0: scalaz.Free.FreeC[Exercises.freelift.Interact,Unit] = Suspend(scalaz.Coyoneda$$anon$22@71423665)
2 Free.liftFC(Ask("how are you")) //> res1: scalaz.Free.FreeC[Exercises.freelift.Interact,String] = Suspend(scalaz.Coyoneda$$anon$22@20398b7c)
我們看看liftFC函數定義:
/** A version of `liftF` that infers the nested type constructor. */
def liftFU[MA](value: => MA)(implicit MA: Unapply[Functor, MA]): Free[MA.M, MA.A] =
liftF(MA(value))(MA.TC)
/** A free monad over a free functor of `S`. */
def liftFC[S[_], A](s: S[A]): FreeC[S, A] =
liftFU(Coyoneda lift s)
Coyoneda lift s 返回結果Coyoneda[S,A], liftFU用Unapply可以把Coyoneda[S,A]轉化成S[A]並提供給liftF。看看Unapply這段:
/**Unpack a value of type `M0[A0, B0]` into types `[a]M0[a, B0]` and `A`, given an instance of `TC` */
implicit def unapplyMAB1[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[α, B0]})#λ]): Unapply[TC, M0[A0, B0]] {
type M[X] = M0[X, B0]
type A = A0
} = new Unapply[TC, M0[A0, B0]] {
type M[X] = M0[X, B0]
type A = A0
def TC = TC0
def leibniz = refl
}
/**Unpack a value of type `M0[A0, B0]` into types `[b]M0[A0, b]` and `B`, given an instance of `TC` */
implicit def unapplyMAB2[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[A0, α]})#λ]): Unapply[TC, M0[A0, B0]] {
type M[X] = M0[A0, X]
type A = B0
} = new Unapply[TC, M0[A0, B0]] {
type M[X] = M0[A0, X]
type A = B0
def TC = TC0
def leibniz = refl
}
好了。但是又想了想,一個是Functor的Interact又是怎樣的呢?那Ask必須返回一個值,而這個值應該是個Free,實際上是代表下一個運算:
1 package Exercises
2 import scalaz._
3 import Scalaz._
4 import scala.language.higherKinds
5 object interact {
6 trait Interact[+A]
7 case class Ask[Next](prompt: String, n: String => Next) extends Interact[Next]
8 case class Tell[Next](msg: String, n: Next) extends Interact[Next]
9
10 object interFunctor extends Functor[Interact] {
11 def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ia match {
12 case Tell(m,n) => Tell(m, f(n))
13 case g: Ask[A] => Ask[B](g.prompt, g.n andThen f)
14 }
15 }
所以Ask返回Next類型值,應該是個Free,代表下一個運算。