Scalaz(33)- Free :算式-Monadic Programming
在任何模式的编程过程中都无法避免副作用的产生。我们可以用F[A]这种类型模拟FP的运算指令:A是可能产生副作用的运算,F[_]是个代数数据类型ADT(Algebraic Data Type),可以实现函数组合(functional composition),我们可以不用理会A,先用F[_]来组合形成描述功能的抽象程序AST(Abstract Syntax Tree),对A的运算可以分开另一个过程去实现,而且可以有多种的运算实现方式,这样就达到了算式AST(Monadic Programming)、算法(Interpretation)的所谓关注分离(separation of concern)目的。在前面的讨论中我们介绍过:我们可以把任何F[A]升格成Monad,而Monad具备最完善的函数组合性能,特别是它支持for-comprehension这种表达方式。我们可以在for-comprehension框架里进行我们熟悉的行令编程(imperative programming),可以使程序意思表达更加显而易见。
下面我们来做一个简单的示范:模拟一个互动智力算数测试(math quiz):在系统提示下,用户输入第一个数字、再输入第二个数字、再输入操作符号、系统输出算数操作结果。我们可以设计ADT如下:
sealed trait Quiz[+Next]
case class Question[Next](que: String, n: String => Next) extends Quiz[Next]
case class Answer[Next](ans: String, n: Next) extends Quiz[Next]
Quiz类型可能属于Question或Answer。Question需要读取一个String类型输入,由于实际需要的可能是一个Int或者是Char,在获取输入后还要进行下一步类型转换(map),所以还必须把一个转换函数String=>Next存放入Question结构。Answer则不需要任何输入,所以我们会把()作为Next的值存入Answer结构。
我们可以map over Next类型获取Quiz的Functor实例:
implicit object QFunctor extends Functor[Quiz] {
def map[A,B](qa: Quiz[A])(f: A => B): Quiz[B] =
qa match {
case q: Question[A] => Question(q.que, q.n andThen f)
case Answer(a,n) => Answer(a,f(n))
}
}
从case q: Question[A]可以看出来:map over Next实际上是连续运算(andThen)。
我们再来几个操作帮助方法:
//操作帮助方法helper methods
def askNumber(q: String) = Question(q, (inputString => inputString.toInt)) //_.toInt
def askOperator(q: String) = Question(q, (inputString => inputString.head.toUpper.toChar))
def answer(fnum: Int, snum: Int, opr: Char) = {
def result =
opr match {
case 'A' => fnum + snum
case 'M' => fnum * snum
case 'D' => fnum / snum
case 'S' => fnum - snum
}
Answer("my answer is: " + result.toString,())
}
我们现在可以这样编写AST了:
import Quiz._
val prg = for {
fn <- askNumber("The first number is:")
sn <- askNumber("The second number is:")
op <- askOperator("The operation is:")
_ <- answer(fn,sn,op)
} yield() //> prg : scalaz.Free[Exercises.interact.Quiz,Unit] = Gosub()
但是,askNumber,askOperator及answer这几个操作函数都返回了Quiz类型,而Quiz类型不是Monad,不支持for-comprehension。我们可以用个隐式转换把所有Quiz[A]升格成Free[Quiz,A]:
implicit def quizToFree[A](qz: Quiz[A]): Free[Quiz,A] = Free.liftF(qz)
这个示范完整的源代码如下:
sealed trait Quiz[+Next]
object Quiz {
//问题que:String, 等待String 然后转成数字或操作符号
case class Question[Next](que: String, n: String => Next) extends Quiz[Next]
case class Answer[Next](ans: String, n: Next) extends Quiz[Next]
implicit object QFunctor extends Functor[Quiz] {
def map[A,B](qa: Quiz[A])(f: A => B): Quiz[B] =
qa match {
case q: Question[A] => Question(q.que, q.n andThen f)
case Answer(a,n) => Answer(a,f(n))
}
}
//操作帮助方法helper methods
def askNumber(q: String) = Question(q, (inputString => inputString.toInt)) //_.toInt
def askOperator(q: String) = Question(q, (inputString => inputString.head.toUpper.toChar)) //_.head.toUpper.toChar
def answer(fnum: Int, snum: Int, opr: Char) = {
def result =
opr match {
case 'A' => fnum + snum
case 'M' => fnum * snum
case 'D' => fnum / snum
case 'S' => fnum - snum
}
Answer("my answer is: " + result.toString,())
}
implicit def quizToFree[A](qz: Quiz[A]): Free[Quiz,A] = Free.liftF(qz)
}
import Quiz._
val prg = for {
fn <- askNumber("The first number is:")
sn <- askNumber("The second number is:")
op <- askOperator("The operation is:")
_ <- answer(fn,sn,op)
} yield() //> prg : scalaz.Free[Exercises.interact.Quiz,Unit] = Gosub()
再看看下面的例子。试着猜测程序的作用:
sealed trait Calc[+A]
object Calc {
case class Push(value: Int) extends Calc[Unit]
case class Add() extends Calc[Unit]
case class Mul() extends Calc[Unit]
case class Div() extends Calc[Unit]
case class Sub() extends Calc[Unit]
implicit def calcToFree[A](ca: Calc[A]) = Free.liftFC(ca)
}
import Calc._
val ast = for {
_ <- Push()
_ <- Push()
_ <- Add()
_ <- Push()
_ <- Mul()
} yield () //> ast : scalaz.Free[[x]scalaz.Coyoneda[Exercises.interact.Calc,x],Unit] = Gosub()
从上面的AST表达方式可以估计到这是一个对Int进行加减乘除的计算器,应该是先通过push把操作对象存入一个Stack。然后对Stack内部的数字进行计算操作。具体是如何实现的,在这个阶段无需知道,这应该是Interpreter的工作。这个例子不就真正体现了算式算法的关注分离了的精髓嘛。
Scalaz(33)- Free :算式-Monadic Programming的更多相关文章
- 泛函编程(24)-泛函数据类型-Monad, monadic programming
在上一节我们介绍了Monad.我们知道Monad是一个高度概括的抽象模型.好像创造Monad的目的是为了抽取各种数据类型的共性组件函数汇集成一套组件库从而避免重复编码.这些能对什么是Monad提供一个 ...
- FP又称为Monadic Programming
什么是Monad? trait Monad[+T] { def flatMap[U]( f : (T) => Monad[U] ) : Monad[U] def unit(value : B) ...
- Scalaz(42)- Free :FreeAp-Applicative Style Programming Language
我们在前面花了几期时间讨论Free Monad,那是因为FP既是Monadic programming,Free Monad是FP模式编程的主要方式.对我们来说,Free Monad代表着fp从学术探 ...
- Scalaz(38)- Free :Coproduct-Monadic语句组合
很多函数式编程爱好者都把FP称为Monadic Programming,意思是用Monad进行编程.我想FP作为一种比较成熟的编程模式,应该有一套比较规范的操作模式吧.因为Free能把任何F[A]升格 ...
- Scalaz(22)- 泛函编程思维: Coerce Monadic Thinking
马上进入新的一年2016了,来点轻松点的内容吧.前面写过一篇关于用Reader实现依赖注入管理的博文(Scalaz(16)- Monad:依赖注入-Dependency Injection By Re ...
- Scalaz(43)- 总结 :FP就是实用的编程模式
完成了对Free Monad这部分内容的学习了解后,心头豁然开朗,存在心里对FP的疑虑也一扫而光.之前也抱着跟大多数人一样的主观概念,认为FP只适合学术性探讨.缺乏实际应用.运行效率低,很难发展成现实 ...
- Scalaz(37)- Free :实践-DB Transaction free style
我一直在不断的提示大家:FP就是Monadic Programming,是一种特殊的编程风格.在我们熟悉的数据库编程领域能不能实现FP风格呢?我们先设计一些示范例子来分析一下惯用的数据库编程过程: i ...
- Scalaz(36)- Free :实践-Free In Action - 实用体验
在上面几期讨论中我们连续介绍了Free Monad.因为FP是纯函数编程,也既是纯函数的组合集成,要求把纯代码和副作用代码可以分离开来.Free Monad的程序描述(AST)和程序实现(Interp ...
- Scalaz(35)- Free :运算-Trampoline,say NO to StackOverflowError
在前面几次讨论中我们介绍了Free是个产生Monad的最基本结构.它的原理是把一段程序(AST)一连串的运算指令(ADT)转化成数据结构存放在内存里,这个过程是个独立的功能描述过程.然后另一个独立运算 ...
随机推荐
- Android SDK 百度云盘分享链接
SDK百度云盘地址: 链接: http://pan.baidu.com/s/1skSCplF 密码: drq4 使用说明: 这是Android开发所需的sdk,下载并解压后,将解压出的整个文件夹复制或 ...
- 第五节:表单标签的用法——value绑定和修饰符
1.表单标签的用法--value绑定和修饰符 value绑定的写法:v-bind:value 或者简写 :value 修饰符: lazy , Number , trim . 用法如: v-model ...
- 关于json序列化和反序列的问题,没事写个案例,希望能帮到那些需要帮忙的朋友!
现在关于json的读写问题,有许许多多的解决方法,因人而异,根据实际问题去选择自己想要的最容易方法.我觉得自带的Newtonsoft.Json是个不错的选择,随便写两个例子吧! 一:关于简单的json ...
- emmet插件学习,练习中遇到一些问题
emmet插件学习:帮助提高敲代码效率的插件 参考文献:Emmet(Zen coding)HTML代码使用技巧七则http://www.wzsky.net/html/Website/htmlcss/1 ...
- c#编码规范
1 规范目的 --------------------- 3 2 适用范围 --------------------- 3 3 代码注释 --------------------- 3 3.1 ...
- 希望有兴趣的加入,共同为项目智能化管理jar包而努力 第二篇
想听听大家对于我这个想法的一些看法,喷也好,赞也罢,希望留下您宝贵的建议! 有共同想法并且想实现的请入群 2500261120 在使用autort插件时,首先要到autort服务器下载所有 ...
- How Spring Boot Autoconfiguration Magic Works--转
原文地址:https://dzone.com/articles/how-springboot-autoconfiguration-magic-works In my previous post &qu ...
- 讲讲Handler+Looper+MessageQueue 关系
Handler+Looper+MessageQueue这三者的关系其实就是Android的消息机制.这块内容相比开发人员都不陌生,在面试中,或者日常开发中都会碰到,今天就来讲这三者的关系. 概述: H ...
- 基于HT for Web 3D技术快速搭建设备面板
以真实设备为模型,搭建出设备面板,并实时获取设备运行参数,显示在设备面板上,这相比于纯数值的设备监控系统显得更加生动直观.今天我们就在HT for Web的3D技术上完成设备面板的搭建. 我们今天模拟 ...
- HBase单个RegionServer的region数目上限
前言 RegionServer维护Master分配给它的region,处理对这些region的IO请求,负责切分在运行过程中变得过大的region, 由于集群性能( 分配的内存和磁盘是有限的 )有限的 ...