设为首页 加入收藏

TOP

Scalaz(17)- Monad:泛函状态类型-State Monad(一)
2017-10-10 12:13:29 】 浏览:7870
Tags:Scalaz Monad 状态 类型 State

  我们经常提到函数式编程就是F[T]。这个F可以被视为一种运算模式。我们是在F运算模式的壳子内对T进行计算。理论上来讲,函数式程序的运行状态也应该是在这个运算模式壳子内的,也是在F[]内更新的。那么我们就应该像函数式运算T值一样,也有一套函数式更新程序状态的方法。之前我们介绍了Writer Monad。Writer也是在F[]内维护Log的,可以说是一种状态维护方式。但Writer的Log是一种Monoid类型,只支持Semigroup的a|+|b操作,所以只能实现一种两段Log相加累积这种效果。WriterT的款式是这样的:

final case class WriterT[F[_], W, A](run: F[(W, A)]) { self => ...

Writer是WriterT的一个F[_] >>> Id特例,那么它的款式也可以被视作这样:

final case class Writer[W, A](run: (W, A)) { self =>

注意这个(W,A)参数,这是一种典型的函数式编程状态维护方式。因为函数式编程强调使用不可变数据(immutable),所以维护状态的方式就是传入当前状态值W然后必须返回新的状态值。由于Writer是个Monad,通过flatMap可以把状态值W在运算之间连续下去。这点我们可以从WriterT的flatMap函数得出:

  def flatMap[B](f: A => WriterT[F, W, B])(implicit F: Bind[F], s: Semigroup[W]): WriterT[F, W, B] = flatMapF(f.andThen(_.run)) def flatMapF[B](f: A => F[(W, B)])(implicit F: Bind[F], s: Semigroup[W]): WriterT[F, W, B] = writerT(F.bind(run){wa => val z = f(wa._2) F.map(z)(wb => (s.append(wa._1, wb._1), wb._2)) })

以上的flatMapF函数把上一个运算的W与下一个运算的W用Monoid操作结合起来s.append(wa._1,wb._1)。Writer类型款式的一个特点就是这个(W,A)返回类型,就是把状态和运算值传入再同时返回。不过对状态的操作只能局限在Monoid操作。曾经提到过Writer还可以被理解成一种特别的状态维护,只是目标锁定在了Log的更新。那么真正意义的状态类型State Monad又是怎样的呢?我们先看看State是怎样定义的:scalaz/package.scala

  type StateT[F[_], S, A] = IndexedStateT[F, S, S, A] type IndexedState[-S1, S2, A] = IndexedStateT[Id, S1, S2, A] /** A state transition, representing a function `S => (S, A)`. */ type State[S, A] = StateT[Id, S, A]

State是StateT的Id特殊案例,而StateT又是IndexedStateT的S1=S2特殊案例。那我们就从最概括的类型IndexedStateT开始介绍吧。下面是IndexedStateT的定义:scalaz/StateT.scala

trait IndexedStateT[F[_], -S1, S2, A] { self =>
  /** Run and return the final value and state in the context of `F` */
  def apply(initial: S1): F[(S2, A)]

  /** An alias for `apply` */
  def run(initial: S1): F[(S2, A)] = apply(initial)

  /** Calls `run` using `Monoid[S].zero` as the initial state */
  def runZero[S <: S1](implicit S: Monoid[S]): F[(S2, A)] =
    run(S.zero)

  /** Run, discard the final state, and return the final value in the context of `F` */
  def eva l(initial: S1)(implicit F: Functor[F]): F[A] =
    F.map(apply(initial))(_._2)

  /** Calls `eva l` using `Monoid[S].zero` as the initial state */
  def eva lZero[S <: S1](implicit F: Functor[F], S: Monoid[S]): F[A] =
    eva l(S.zero)

  /** Run, discard the final value, and return the final state in the context of `F` */
  def exec(initial: S1)(implicit F: Functor[F]): F[S2] =
    F.map(apply(initial))(_._1)

  /** Calls `exec` using `Monoid[S].zero` as the initial state */
  def execZero[S <: S1](implicit F: Functor[F], S: Monoid[S]): F[S2] =
    exec(S.zero)
...

IndexedStateT的抽象函数是这个apply(initial:S1):F[(S2,A)],它的函数款式是:S1=>F[(S2,A)],意思是传入S1,把结果包在F里以F[(W,A)]返回。如果F[]=Id的话,那就是S1=>(S2,A)了。函数run就是apply,就是一种状态运算函数:传入状态S1,通过运算返回计算值A和新状态S2,并把结果包在F[(S2,A)]里。其它函数都是用来获取新的运算值或新状态的,如:eva l返回F[A],exec返回F[S2]。值得注意的是这个F必须是Functor才行,因为我们必须用map才能在F[]内更新运算值或状态。当然,如果我们使用State类型的话,F就是Id,那么run=>(s,a),eva l=>a,exec=>s。与Writer比较,State Monad通过一个状态运算函数功能要强大得多了,运用也要灵活许多。

我们再来看看IndexedStateT的map,flatMap:

 def map[B](f: A => B)(implicit F: Functor[F]): IndexedStateT[F, S1, S2, B] = IndexedStateT(s => F.map(apply(s)) { case (s1, a) => (s1, f(a)) }) def flatMap[S3, B](f: A => IndexedStateT[F, S2, S3, B])(implicit F: Bind[F]): IndexedStateT[F, S1, S3, B] = I
编程开发网
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(16)- Monad:依赖注入.. 下一篇Scalaz(18)- Monad: ReaderWr..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }