Scalaz(28)- ST Monad :FP方式适用变量
函数式编程模式强调纯代码(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的一些用例:
import scalaz._
import Scalaz._
import effect._
import ST._
object st {
def e1[S] = for {
r <- newVar[S]()
x <- r.mod {_ + }
} yield x //> e1: [S]=> scalaz.effect.ST[S,scalaz.effect.STRef[S,Int]] def e2[S] = for {
r <- newVar[S]()
x <- r.mod {_ + }
y <- x.read
} yield y //> e2: [S]=> scalaz.effect.ST[S,Int] def e3[S] = for {
arr <- newArr[S,Int](,)
_ <- arr.write(,)
_ <- arr.write(,)
_ <- arr.update ((a: Int,b: Int) => a + b, , )
r <- arr.freeze
} yield r //> e3: [S]=> scalaz.effect.ST[S,scalaz.ImmutableArray[Int]]
newVar[S](3)以状态S新建一个Int存放地址并存入值3。mod操作返回ST[S,STRef[S,Int]],返回的是个地址(reference),而read返回的是ST[S,Int],则是个值。首先注意e1[S],e2[S],e3[S]它们都附带了独立状态标签S。
runST(new Forall[({type l[x] = ST[x, Int]})#l]{def apply[S] = e2[S]})
//> res0: Int = 5
//runST(new Forall[({type l[x] = ST[x, Int]})#l]{def apply[S] = e1[S]})
//type mismatch; found : scalaz.effect.ST[S,scalaz.effect.STRef[S,Int]] required: scalaz.effect.ST[S,Int]
/** A universally quantified value */
trait Forall[P[_]] {
def apply[A]: P[A]
}
type ForallST[A] = Forall[({type l[x] = ST[x,A]})#l]
runST(new ForallST[Int]{ def apply[S] = e2[S] }) //> res0: Int = 5
//runST(new ForallST[Int]{def apply[S] = e1[S]})
//type mismatch; found : scalaz.effect.ST[S,scalaz.effect.STRef[S,Int]] required: scalaz.effect.ST[S,Int]
/**Run a state thread */
def runST[A](f: Forall[({type λ[S] = ST[S, A]})#λ]): A =
f.apply.apply(ivoryTower)._2
Scalaz(28)- ST Monad :FP方式适用变量的更多相关文章
- 泛函编程(34)-泛函变量:处理状态转变-ST Monad
泛函编程的核心模式就是函数组合(compositionality).实现函数组合的必要条件之一就是参与组合的各方程序都必须是纯代码的(pure code).所谓纯代码就是程序中的所有表达式都必须是Re ...
- Ansible系列(六):各种变量定义方式和变量引用
本文目录:1.1 ansible facts1.2 变量引用json数据的方式 1.2.1 引用json字典数据的方式 1.2.2 引用json数组数据的方式 1.2.3 引用facts数据1.3 设 ...
- Ansible系列(五):各种变量定义方式和变量引用
Ansible系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html 1.1 ansible facts facts组件是用来收集被管理节点信息的 ...
- 【转】 IOS,objective_C中用@interface和 @property 方式声明变量的区别
原文: http://blog.csdn.net/ganlijianstyle/article/details/7924446 1.在 @interface :NSObject{} 的括号中,当然N ...
- 初识 Javascript.01 -- Javascript基础|输出方式、变量、变量命名规范、数据类型、
Javascript基础 1 聊聊Javascript 1.1 Javascript的历史来源 94年网景公司 研发出世界上第一款浏览器. 95年 sun公司 java语言诞生 网景公司和su ...
- 基础知识:编程语言介绍、Python介绍、Python解释器安装、运行Python解释器的两种方式、变量、数据类型基本使用
2018年3月19日 今日学习内容: 1.编程语言的介绍 2.Python介绍 3.安装Python解释器(多版本共存) 4.运行Python解释器程序两种方式.(交互式与命令行式)(♥♥♥♥♥) 5 ...
- python基础(内存分析,不引入第三方变量的方式交换变量的值)
a,b指向同一块内存地址 下面方法是重新给b赋值;a,b指向不同的内存地址 字符串或int类型内存分析 不引入第三方变量的方式,交换a,b的值
- PythonDay02——编程语言、python介绍以及安装解释器、运行程序的两种方式、变量
一.编程语言 1.1 机器语言:直接用计算机能理解的二进制指令编写程序,直接控制硬件 1.2 汇编语言:用英文标签取代二进制指令去编写程序,本质也是直接控制硬件 1.3 高级语言:用人能理解的表达方式 ...
- day2 编程语言介绍、Python运行程序的两种方式、变量
一 编程语言介绍 1. 机器语言 用计算机能理解的二进制指令直接编写程序,直接控制硬件 2. 汇编语言 用英文标签取代二进制指令编写程序,本质也是直接控制硬件 3. 高级语言 用人能理解的表达方式去编 ...
随机推荐
- iOS---类方法(静态方法)和实例方法
类方法 实例方法是以+开头的方法, 实例方法是用实例对象访问: 类方法的对象是类而不是实例,通常用来创建对象或者工具类. 在实例方法里,根据继承原理发送消息给self和super其实都 ...
- java 线程的终止与线程中断
关于线程终止: 1.一般来讲线程在执行完毕后就会进入死亡状态,那该线程自然就终止了. 2.一些服务端的程序,可能在业务上需要,常驻系统.它本身是一个无穷的循环,用于提供服务.那对于这种线程我们该如何结 ...
- Ext2.x学习笔记
Ext2.X学习笔记一 一.ExtJS简介 1.1 什么是Ext JS? · Ext JS是一个Ajax框架,可以用来开发富客户端的Ajax应用,是一个用javascript写的,主要用于创建前端用 ...
- SQL Server中关于跟踪(Trace)那点事
前言 一提到跟踪俩字,很多人想到警匪片中的场景,同样在我们的SQL Server数据库中“跟踪”也是无处不在的,如果我们利用好了跟踪技巧,就可以针对某些特定的场景做定向分析,找出充足的证据来破案. 简 ...
- .NET面试题解析(06)-GC与内存管理
系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 GC作为.NET的重要核心基础,是必须要了解的.本文主要侧重于GC内存管理中的一些关键点,如要要全面深入了 ...
- 菜鸟级别的WCF入门学习
这两天学习WCF,看了MSDN上的入门教程,和查找了一些网上给的例子,简单的实现了一下,感觉应该很适合我这种菜鸟级的选手看了. 1.新建一个项目--WCF--WCF服务应用程序 用的是MSDN上的加减 ...
- CSS清浮动
× 目录 [1]定义 [2]方法 [3]兼容 前面的话 人们经常谈起清浮动,其实就是解决浮动元素的包含块高度塌陷的问题 定义 clear 清除 值: left | right | both | non ...
- Java面试题技术类一
1.面向对象编程的三大特性是什么? (1).继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的一个新类可以从现有的类中派生,这个过程称为类继承.新类继 ...
- Android UI设计中一些不错的示例及第三方控件
1.二级ListView自定义布局ExpandableListView http://pan.baidu.com/s/1mhlJh12 密码:nhc2 2.ListView实现各种动画效果ListVi ...
- 牛顿法与拟牛顿法学习笔记(四)BFGS 算法
机器学习算法中经常碰到非线性优化问题,如 Sparse Filtering 算法,其主要工作在于求解一个非线性极小化问题.在具体实现中,大多调用的是成熟的软件包做支撑,其中最常用的一个算法是 L-BF ...