设为首页 加入收藏

TOP

Scalaz(28)- ST Monad :FP方式适用变量(一)
2017-10-10 12:13:18 】 浏览:8934
Tags:Scalaz Monad 方式 适用 变量

    函数式编程模式强调纯代码(pure code),主要实现方式是使用不可变数据结构,目的是函数组合(composability)最终实现函数组件的重复使用。但是,如果我们在一个函数p内部使用了可变量(mutable variables),如果函数的输入参数e是纯代码,那么表达式p(e)同样是纯代码的,因为函数的调用者是无法接触到函数内部申明的这些可变量的。不过,这样的做法会造成函数的臃肿代码,因为在函数内部是无法实现函数组合的,无法重复使用函数组件,实际上又违背了FP的宗旨。Scalaz提供了专门解决可变量使用问题的方法,能保证即使在并行运算的环境内各线程无法影响相互间的可变量,即ST Monad。

Scalaz的可变量分两种:一个内存地址STRef或一个可变数组STArray。我们先看看它们在源代码中的定义:effect/ST.scala

/**Mutable variable in state thread S containing a value of type A. [[http://research.microsoft.com/en-us/um/people/simonpj/papers/lazy-functional-state-threads.ps.Z]] */
sealed trait STRef[S, A] { protected var value: A /**Reads the value pointed at by this reference. */ def read: ST[S, A] = returnST(value) /**Modifies the value at this reference with the given function. */ def mod[B](f: A => A): ST[S, STRef[S, A]] = st((s: Tower[S]) => { value = f(value); (s, this) }) /**Associates this reference with the given value. */ def write(a: => A): ST[S, STRef[S, A]] = st((s: Tower[S]) => { value = a; (s, this) }) /**Synonym for write*/ def |=(a: => A): ST[S, STRef[S, A]] = write(a) /**Swap the value at this reference with the value at another. */ def swap(that: STRef[S, A]): ST[S, Unit] = for { v1 <- this.read v2 <- that.read _ <- this write v2 _ <- that write v1 } yield () } ... /**Mutable array in state thread S containing values of type A. */
sealed trait STArray[S, A] { def size: Int def z: A implicit def manifest: Manifest[A] private lazy val value: Array[A] = Array.fill(size)(z) import ST._ /**Reads the value at the given index. */ def read(i: Int): ST[S, A] = returnST(value(i)) /**Writes the given value to the array, at the given offset. */ def write(i: Int, a: A): ST[S, STArray[S, A]] = st(s => { value(i) = a; (s, this) }) /**Turns a mutable array into an immutable one which is safe to return. */ def freeze: ST[S, ImmutableArray[A]] = st(s => (s, ImmutableArray.fromArray(value))) /**Fill this array from the given association list. */ def fill[B](f: (A, B) => A, xs: Traversable[(Int, B)]): ST[S, Unit] = xs match { case Nil             => returnST(()) case ((i, v) :: ivs) => for { _ <- update(f, i, v) _ <- fill(f, ivs) } yield () } /**Combine the given value with the value at the given index, using the given function. */ def update[B](f: (A, B) => A, i: Int, v: B) = for { x <- read(i) _ <- write(i, f(x, v)) } yield () }

我们看到STRef和STArray都定义了write,mod,update这样有副作用的操作函数,它们都返回了ST[S,STRef[S,A]]类型的结果。ST是个Monad,我们可以从源代码中证实: 

/** * Purely functional mutable state threads. * Based on JL and SPJ's paper "Lazy Functional State Threads" */
sealed trait ST[S, A] { private[effect] def apply(s: Tower[S]): (Tower[S], A) import ST._ def flatMap[B](g: A => ST[S, B]): ST[S, B] = st(s => apply(s) match { case (ns, a) => g(a)(ns) }) def map[B](g: A => B): ST[S, B] = st(s => apply(s) match { case (ns, a) => (ns, g(a)) }) }

ST与State Monad极其相似,备有map和flatMap,所以是个Monad,能支持for-comprehension。我们可以通过ST的for-comprehension实现STRef,STArray操作函数的组合,因为这些操作函数的返回结果都是ST类型的。但write,mod这些操作函数有个共同的奇怪现象:它们都没有调用过S类型的值,直接按传入就输出去了。这正是ST Monad如何命名的:ST又可以被称为State Tag,也就是说每一项操作都有独立的状态类型S,如果S类型有所不同的话是无法调用操作函数的。而for-comprehension是一种串型流程,能保证线程之间不会交叉运行,相互影响各自的可变量。ST Monad与State Monad最大的不同是它没有run方法,也就是我们无法用ST的内部方法来获取ST[S,A]的A值。我们先看看STRef和STArray的一些用例:

 

 1 import scalaz._  2 import Scalaz._  3 import effect._  4 import ST._  5 object st {  6 def e1[S] = for {  7   r <- n
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇atitit.编程语言?类与对象的?扩展.. 下一篇kafka使用getOffsetsBefore()获取..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目