很多时候我们会遇到一些高阶类型F[_],但又无法实现它的map函数,也就是虽然形似但F不可能成为Functor.看看下面的例子: trait Interact[A] case class Ask(prompt: String) extends Interact[String] case class Tell(msg: String) extends Interact[Unit] Interact类型只容许两种实例:Ask继承了Interact[String], Tell继承Interact[Un…
中间插播了几篇scalaz数据类型,现在又要回到Monad专题.因为FP的特征就是Monad式编程(Monadic programming),所以必须充分理解认识Monad.熟练掌握Monad运用.曾经看到一段对Monad的描述:“Monadic for-comprehension就是一种嵌入式编程语言,由它的Monad提供它的语法”.但如果每一种Monad的for-comprehension都独立提供一套语法的话,这种编程语言就显得十分单调.功能简单了.那么既然是FP,我们应该可以通过函数组合…
在上一篇讨论里我在设计示范例子时遇到了一些麻烦.由于Free Monad可能是一种主流的FP编程规范,所以在进入实质编程之前必须把所有东西都搞清楚.前面遇到的问题主要与scalaz Free的FreeC类型有关系.这个类型主要是针对一些非Functor的F[A]特别设计的.FreeC是Coyoneda[F,A]的Free Monad类型,任何F[A]都可以被转换成Coyoneda[F,A],而Coyoneda[F,A]本身是个Functor.因为我们通常都在Functor和非Functor AD…
一直感觉FP比较虚,可能太多学术性的东西,不知道如何把这些由数学理论在背后支持的一套全新数据类型和数据结构在现实开发中加以使用.直到Free Monad,才真正感觉能用FP方式进行编程了.在前面我们已经花了不小篇幅来了解Free Monad,这次我想跟大家讨论一下用Free Monad来编写一个真正能运行的完整应用程序.当然,这个程序必须具备FP特性,比如函数组合(function composition),纯代码(pure code),延迟副作用(delayed side effect)等等.…
我们说过自由数据结构(free structures)是表达数据类型的最简单结构.List[A]是个数据结构,它是生成A类型Monoid的最简单结构,因为我们可以用List的状态cons和Nil来分别代表Monoid的append和zero.Free[S,A]是个代表Monad的最简单数据结构,它可以把任何Functor S升格成Monad.Free的两个结构Suspend,Return分别代表了Monad的基本操作函数flatMap,point,我特别强调结构的意思是希望大家能意识到那就是内存…
在任何模式的编程过程中都无法避免副作用的产生.我们可以用F[A]这种类型模拟FP的运算指令:A是可能产生副作用的运算,F[_]是个代数数据类型ADT(Algebraic Data Type),可以实现函数组合(functional composition),我们可以不用理会A,先用F[_]来组合形成描述功能的抽象程序AST(Abstract Syntax Tree),对A的运算可以分开另一个过程去实现,而且可以有多种的运算实现方式,这样就达到了算式AST(Monadic Programming)…
在前几篇关于Functor和Applilcative typeclass的讨论中我们自定义了一个类型Configure,Configure类型的定义是这样的: case class Configure[+A](get: A) object Configure { implicit val configFunctor = new Functor[Configure] { def map[A,B](ca: Configure[A])(f: A => B): Configure[B] = Config…
scala已经配备了自身的Future类.我们先举个例子来了解scala Future的具体操作: import scala.concurrent._ import ExecutionContext.Implicits.global object scalafuture { def dbl(i: Int): Future[Int] = Future { Thread.sleep() ; i + i } //> dbl: (i: Int)scala.concurrent.Future[Int] v…
完成了对Free Monad这部分内容的学习了解后,心头豁然开朗,存在心里对FP的疑虑也一扫而光.之前也抱着跟大多数人一样的主观概念,认为FP只适合学术性探讨.缺乏实际应用.运行效率低,很难发展成现实的软件开发模式.Free Monad的出现恰恰解决我心中的疑问,更正了我对FP的偏见:Free Monad提供了一套在Monad 算法内(在 for-comprehension内)的行令编程(imperative programming)方法,解决了FP的复杂语法,使Monadic编程更贴近传统编程…
我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码.像通过键盘显示器进行交流.读写文件.数据库等这些IO操作都会产生副作用.那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段烧CPU的代码,没有任何意义,所以任何类型的程序都必须具备IO功能,而在FP模式中对IO操作有特别的控制方式:具体实现是通过把代码中产生副作用的部分抽离出来延后运算(在所有纯代码运算之后).scalaz的IO Monad就是处理副作用代码延后运算的一种数据结构.我先举个简单的例子来示范如何通过一种数…
很多函数式编程爱好者都把FP称为Monadic Programming,意思是用Monad进行编程.我想FP作为一种比较成熟的编程模式,应该有一套比较规范的操作模式吧.因为Free能把任何F[A]升格成Monad,所以Free的算式(AST).算法(Interpreter)关注分离(separation of concern)模式应该可以成为一种规范的FP编程模式.我们在前面的几篇讨论中都涉及了一些AST的设计和运算,但都是一些功能单一,离散的例子.如果希望通过Free获取一个完整可用的程序,就…
我一直在不断的提示大家:FP就是Monadic Programming,是一种特殊的编程风格.在我们熟悉的数据库编程领域能不能实现FP风格呢?我们先设计一些示范例子来分析一下惯用的数据库编程过程: import scalaz._ import Scalaz._ import scala.language.higherKinds import scala.language.implicitConversions import com.jolbox.bonecp.BoneCP import com.…
在上面几期讨论中我们连续介绍了Free Monad.因为FP是纯函数编程,也既是纯函数的组合集成,要求把纯代码和副作用代码可以分离开来.Free Monad的程序描述(AST)和程序实现(Interpretation)关注分离(separation of concern)模式恰恰能满足FP要求.我们可以用一些代数数据类型(ADT Algebraic Data Type)来模拟功能,再把这些ADT组合起来形成AST(Abstract Syntax Tree).AST既是对程序功能的描述,它的组成过…
在前面几次讨论中我们介绍了Free是个产生Monad的最基本结构.它的原理是把一段程序(AST)一连串的运算指令(ADT)转化成数据结构存放在内存里,这个过程是个独立的功能描述过程.然后另一个独立运算过程的Interpreter会遍历(traverse)AST结构,读取结构里的运算指令,实际运行指令.这里的重点是把一连串运算结构化(reify)延迟运行,具体实现方式是把Monad的连续运算方法flatMap转化成一串Suspend结构(case class),把运算过程转化成创建(constru…
我们可以通过自由数据结构(Free Structure)实现对程序的算式和算法分离关注(separation of concern).算式(Abstract Syntax Tree, AST)即运算表达式,是对程序功能的描述.算法则是程序的具体运算方式(Interpreter),它赋予了算式意义.下面我们先用一个例子简单解释何为算式.算法: 用一个简单的表达式 1+2+3,这个表达式同时包含了算式和算法:运算表达式是 a Op b Op c, 算法是:Int加法,a,b,c为Int, oP为In…
scala中的case class是一种特殊的对象:由编译器(compiler)自动生成字段的getter和setter.如下面的例子: case class City(name:String, province: String) case class Address(street: String, zip: String, city: City) case class Person(name: String, age: Int, phone: String, address: Address)…
scala标准库提供了一个Either类型,它可以说是Option的升级版.与Option相同,Either也有两种状态:Left和Right,分别对应Option的None和Some,不同的是Left可以返回一个值.我们通常用这个值来表述异常信息.scalaz也提供了自己版本的Either,并用\/来分辨表示,以及两种状态-\/和\/-.我想scalaz特别提供\/是有原因的:\/不单是一种类型,它是一种type class.更重要的是\/是一种Monad,具备了函数组合能力(composib…
说道FP,我们马上会联想到Monad.我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串型的流程(workflow).更直白的讲法是:任何类型只要实现了flatMap就可以用for-comprehension, for{...}yield.在这个for{...}里我们可以好像OOP一样编写程序.这个for就是一种运算模式,它规范了在for{...}里指令的行为.我们正从OOP风格走入FP编程模式,希望有个最基本的FP编程模式使我…
我们经常提到函数式编程就是F[T].这个F可以被视为一种运算模式.我们是在F运算模式的壳子内对T进行计算.理论上来讲,函数式程序的运行状态也应该是在这个运算模式壳子内的,也是在F[]内更新的.那么我们就应该像函数式运算T值一样,也有一套函数式更新程序状态的方法.之前我们介绍了Writer Monad.Writer也是在F[]内维护Log的,可以说是一种状态维护方式.但Writer的Log是一种Monoid类型,只支持Semigroup的a|+|b操作,所以只能实现一种两段Log相加累积这种效果.…
在上一篇讨论里我们简单的介绍了一下Cake Pattern和Reader Monad是如何实现依赖注入的.主要还是从方法上示范了如何用Cake Pattern和Reader在编程过程中解析依赖和注入依赖.考虑到依赖注入模式在编程中的重要性和普遍性,觉着还需要再讨论的深入一些,使依赖注入模式在FP领域里能从理论走向实际.既然我们正在scalaz的介绍系列里,所以这篇我们就着重示范Reader Monad的依赖注入方法. 再说明一下依赖注入:我们说过在团队协作开发里能够实现软件模块的各自独立开发,原…
我们可以用Monad Reader来实现依赖注入(dependency injection DI or IOC)功能.Scala界中比较常用的不附加任何Framework的依赖注入方式可以说是Cake Pattern了.现在通过Monad Reader可以实现同样功能,两者对比优点各有千秋.所谓依赖注入是指在编程时使用了某个未知实现细节的对象,但依赖注入确保这个对象在这段程序运行时已经实例化.这种需求通常是在大型软件开发时对项目进行模块化分割后虽然模块之间互有依赖,但又可以同步开发.特别是在多人…
Monad Reader就是一种函数的组合.在scalaz里函数(function)本身就是Monad,自然也就是Functor和applicative.我们可以用Monadic方法进行函数组合: import scalaz._ import Scalaz._ object decompose { //两个测试函数 val f = (_: Int) + //> f : Int => Int = <function1> val g = (_: Int) * //> g : In…
通过前面的几篇讨论我们了解到F[T]就是FP中运算的表达形式(representation of computation).在这里F[]不仅仅是一种高阶类型,它还代表了一种运算协议(computation protocol)或者称为运算模型好点,如IO[T],Option[T].运算模型规范了运算值T的运算方式.而Monad是一种特殊的FP运算模型M[A],它是一种持续运算模式.通过flatMap作为链条把前后两个运算连接起来.多个flatMap同时作用可以形成一个程序运行链.我们可以在flat…
在前面的几篇讨论里我们初步对FP有了些少了解:FP嘛,不就是F[A]吗?也是,FP就是在F[]壳子(context)内对程序的状态进行更改,也就是在F壳子(context)内施用一些函数.再直白一点就是在F壳子内进行OOP惯用的行令编程(imperative programming).当然,既然是在壳子(context)内进行编程这种新的模式,那么总需要些新的函数施用方法吧.我们再次审视一下以前了解过的FP函数施用方法: // Functor : map[A,B] (F[A])(f: A =>…
前面提到了scalaz是个函数式编程(FP)工具库.它提供了许多新的数据类型.拓展的标准类型及完整的一套typeclass来支持scala语言的函数式编程模式.我们知道:对于任何类型,我们只需要实现这个类型的typeclass实例就可以在对这个类型施用所对应typeclass提供的所有组件函数了(combinator).突然之间我们的焦点好像都放在了如何获取typeclass实例上了,从而忽略了考虑为什么要使用这些typeclass及使用什么样的typeclass这些问题了.所以可能有人会问我:…
Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming.这其中透露的Monad重要性则不言而喻.Scalaz是通过Monad typeclass为数据运算的程序提供了一套规范的编程方式,如常见的for-comprehension.而不同类型的Monad实例则会支持不同的程序运算行为,如:Option Monad在运算中如果遇到None值则会中途退出:State…
Monoid是种最简单的typeclass类型.我们先看看scalaz的Monoid typeclass定义:scalaz/Monoid.scala trait Monoid[F] extends Semigroup[F] { self => //// /** The identity element for `append`. */ def zero: F ... Monoid trait又继承了Semigroup:scalaz/Semigroup.scala trait Semigroup[…
Applicative,正如它的名称所示,就是FP模式的函数施用(function application).我们在前面的讨论中不断提到FP模式的操作一般都在管道里进行的,因为FP的变量表达形式是这样的:F[A],即变量A是包嵌在F结构里的.Scalaz的Applicative typeclass提供了各种类型的函数施用(function application)和升格(lifting)方法.与其它scalaz typeclass使用方式一样,我们只需要实现了针对自定义类型的Applicativ…
Functor是范畴学(Category theory)里的概念.不过无须担心,我们在scala FP编程里并不需要先掌握范畴学知识的.在scalaz里,Functor就是一个普通的typeclass,具备map over特性.我的理解中,Functor的主要用途是在FP过程中更新包嵌在容器(高阶类)F[T]中元素T值.典型例子如:List[String], Option[Int]等.我们曾经介绍过FP与OOP的其中一项典型区别在于FP会尽量避免中间变量(temp variables).FP的变…
我们在前面花了几期时间讨论Free Monad,那是因为FP既是Monadic programming,Free Monad是FP模式编程的主要方式.对我们来说,Free Monad代表着fp从学术探讨到实际应用的转变,因为我们已经示范了如何用Free Monad的算式算法关注分离模式来实现真正的软件编程.但是美中不足的是用Free Monad只能编写流程式的程序:我们只能一步一步编译这种程序而无法实现并行运算以及在编译之前对程序结果进行分析或转换等.这种特性可以从Monad的运算函数flatM…