Scalaz(32)- Free :lift - Monad生产线
在前面的讨论里我们提到自由数据结构就是产生某种类型的最简化结构,比如:free monoid, free monad, free category等等。我们也证明了List[A]是个free monoid。我们再看看free monad结构Free的定义:scalaz/Free.scala
/** A free operational monad for some functor `S`. Binding is done using the heap instead of the stack,
* allowing tail-call elimination. */
sealed abstract class Free[S[_], A] {
...
/** Return from the computation with the given value. */
private[scalaz] case class Return[S[_], A](a: A) extends Free[S, A] /** Suspend the computation with the given suspension. */
private[scalaz] case class Suspend[S[_], A](a: S[Free[S, A]]) extends Free[S, A]
...
我们在上一篇里证明过Free就是free monad,因为Free是个Monad而且它的结构是最简单的了:
1、Free[S[_],A]可以代表一个运算
2、case class Return是一个数据结构。Return(a:A)代表运算结束,运算结果a存放在结构中。另一个意义就是Monad.point(a:A),把一个任意A值a升格成Free
3、case class Suspend也是另一个数据结构。Suspend(a: S[Free[S,A]])代表把下一个运算存放在结构中。如果用Monad.join(a: F[F[A]])表示,那么里面的F[A]应该是个Free[S,A],这样我们才可能把运算结束结构Return[S,A](a:A)存放到Suspend中来代表下一步结束运算。
注意,Suspend(a: S[Free[S,A]])是个递归类型,S必须是个Functor,但不是任何Functor,而是map over Free[S,A]的Functor,也就是运算另一个Free[S,A]值。如果这个Free是Return则返回运算结果,如果是Suspend则继续递归运算。
简单来说Free是一个把Functor S[_]升格成Monad的产生器。我们可以用Free.liftF函数来把任何一个Functor升格成Monad。看看Free.liftF的函数款式就知道了:
/** Suspends a value within a functor in a single step. Monadic unit for a higher-order monad. */
def liftF[S[_], A](value: => S[A])(implicit S: Functor[S]): Free[S, A] =
Suspend(S.map(value)(Return[S, A]))
liftF可以把一个S[A]升格成Free[S,A]。我们用个例子来证明:
package Exercises
import scalaz._
import Scalaz._
import scala.language.higherKinds
import scala.language.implicitConversions
object freelift {
trait Config[+A] {
def get: A
}
object Config {
def apply[A](a: A): Config[A] = new Config[A] { def get = a}
implicit val configFunctor = new Functor[Config] {
def map[A,B](ca: Config[A])(f: A => B) = Config[B](f(ca.get))
}
} val freeConfig = Free.liftF(Config("hi config")) //> freeConfig : scalaz.Free[Exercises.freelift.Config,String] = Suspend(Exercises.freelift$Config$$anon$2@d70c109)
在上面的例子里Config是个运算A值的Functor。我们可以用Free.liftF把Config(String)升格成Free[Config,String]。实际上我们可以把这个必须是Functor的门槛取消,因为用Coyoneda就可以把任何F[A]拆解成Coyoneda[F,A],而Coyoneda天生是个Functor。我们先看个无法实现map函数的F[A]:
trait Interact[+A] //Console交互
//println(prompt: String) then readLine 返回String
case class Ask(prompt: String) extends Interact[String]
//println(msg: String) 不反回任何值
case class Tell(msg: String) extends Interact[Unit]
由于Ask和Tell都不会返回泛类值,所以无需或者无法实现map函数,Interact[A]是个不是Functor的高阶类。我们必须把它转成Coyoneda提供给Free产生Monad:
Free.liftFC(Tell("hello")) //> res0: scalaz.Free.FreeC[Exercises.freelift.Interact,Unit] = Suspend(scalaz.Coyoneda$$anon$22@71423665)
Free.liftFC(Ask("how are you")) //> res1: scalaz.Free.FreeC[Exercises.freelift.Interact,String] = Suspend(scalaz.Coyoneda$$anon$22@20398b7c)
我们看看liftFC函数定义:
/** A version of `liftF` that infers the nested type constructor. */
def liftFU[MA](value: => MA)(implicit MA: Unapply[Functor, MA]): Free[MA.M, MA.A] =
liftF(MA(value))(MA.TC) /** A free monad over a free functor of `S`. */
def liftFC[S[_], A](s: S[A]): FreeC[S, A] =
liftFU(Coyoneda lift s)
Coyoneda lift s 返回结果Coyoneda[S,A], liftFU用Unapply可以把Coyoneda[S,A]转化成S[A]并提供给liftF。看看Unapply这段:
/**Unpack a value of type `M0[A0, B0]` into types `[a]M0[a, B0]` and `A`, given an instance of `TC` */
implicit def unapplyMAB1[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[α, B0]})#λ]): Unapply[TC, M0[A0, B0]] {
type M[X] = M0[X, B0]
type A = A0
} = new Unapply[TC, M0[A0, B0]] {
type M[X] = M0[X, B0]
type A = A0
def TC = TC0
def leibniz = refl
} /**Unpack a value of type `M0[A0, B0]` into types `[b]M0[A0, b]` and `B`, given an instance of `TC` */
implicit def unapplyMAB2[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[A0, α]})#λ]): Unapply[TC, M0[A0, B0]] {
type M[X] = M0[A0, X]
type A = B0
} = new Unapply[TC, M0[A0, B0]] {
type M[X] = M0[A0, X]
type A = B0
def TC = TC0
def leibniz = refl
}
好了。但是又想了想,一个是Functor的Interact又是怎样的呢?那Ask必须返回一个值,而这个值应该是个Free,实际上是代表下一个运算:
package Exercises
import scalaz._
import Scalaz._
import scala.language.higherKinds
object interact {
trait Interact[+A]
case class Ask[Next](prompt: String, n: String => Next) extends Interact[Next]
case class Tell[Next](msg: String, n: Next) extends Interact[Next] object interFunctor extends Functor[Interact] {
def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ia match {
case Tell(m,n) => Tell(m, f(n))
case g: Ask[A] => Ask[B](g.prompt, g.n andThen f)
}
}
所以Ask返回Next类型值,应该是个Free,代表下一个运算。
Scalaz(32)- Free :lift - Monad生产线的更多相关文章
- 泛函编程(30)-泛函IO:Free Monad-Monad生产线
在上节我们介绍了Trampoline.它主要是为了解决堆栈溢出(StackOverflow)错误而设计的.Trampoline类型是一种数据结构,它的设计思路是以heap换stack:对应传统递归算法 ...
- Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言
说道FP,我们马上会联想到Monad.我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串型的流程(workflow).更直白的讲法是 ...
- Scalaz(11)- Monad:你存在的意义
前面提到了scalaz是个函数式编程(FP)工具库.它提供了许多新的数据类型.拓展的标准类型及完整的一套typeclass来支持scala语言的函数式编程模式.我们知道:对于任何类型,我们只需要实现这 ...
- 泛函编程(33)-泛函IO:Free Functor - Coyoneda
在前几期讨论中我们终于推导出了Free Monad.这是一个Monad工厂,它可以把任何F[A]变成Monad.可惜的是它对F[A]是有所要求的:F必须是个Functor.Free Monad由此被称 ...
- Haskell语言学习笔记(22)MaybeT
Monad Transformers Monad 转换器用于将两个不同的Monad合成为一个Monad.Monad 转换器本身也是一个 Monad. MaybeT MaybeT 这个 Monad 转换 ...
- Scalability, Availability & Stability Patterns
https://blog.csdn.net/ajian005/article/details/6191814 一 自我有要求的读者应该提出问题:(研习:掌握层次:)能力级别:不会(了解)——领会( ...
- Scalaz(25)- Monad: Monad Transformer-叠加Monad效果
中间插播了几篇scalaz数据类型,现在又要回到Monad专题.因为FP的特征就是Monad式编程(Monadic programming),所以必须充分理解认识Monad.熟练掌握Monad运用.曾 ...
- Scalaz(17)- Monad:泛函状态类型-State Monad
我们经常提到函数式编程就是F[T].这个F可以被视为一种运算模式.我们是在F运算模式的壳子内对T进行计算.理论上来讲,函数式程序的运行状态也应该是在这个运算模式壳子内的,也是在F[]内更新的.那么我们 ...
- Scalaz(41)- Free :IO Monad-Free特定版本的FP语法
我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码.像通过键盘显示器进行交流.读写文件.数据库等这些IO操作都会产生副作用.那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段 ...
随机推荐
- [Java面试一]面试复习大纲.
一.Java基础部分 (搞定所有技术之后才考虑复习的技术点) 1.数组中的排序问题(笔试或者机试,前者可能性更大) 2.面向对象的理解 3.集合相关的问题,比如hashmap跟hashtable的区别 ...
- Atitit 图像处理 灰度图片 灰度化的原理与实现
Atitit 图像处理 灰度图片 灰度化的原理与实现 24位彩色图与8位灰度图 首先要先介绍一下24位彩色图像,在一个24位彩色图像中,每个像素由三个字节表示,通常表示为RGB.通常,许多24位彩色图 ...
- fir.im Weekly - 进击的 Swift
最近 Swift 开源了,众开发者们欢呼雀跃.感谢开源,这是最好的时代.本期 fir.im Weekly 准备了一些关于 Swift 的"新鲜"干货分享,也包括一些优秀的 GitH ...
- iOS-Objective-C内存管理
内存管理: 栈区 [stack]:由编译器自动分配并释放,一般存放函数的参数值,局部变量等 堆区 [heap]:由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 全局区(静态区) ...
- jQuery_03之事件、动画、类数组操作
一.事件: 1.模式触发事件: ①DOM:elem.onXXX();只能触发直接用onXXX绑定的事件处理函数:用addEventistener添加的事件监听无法模拟出发触发: ②jQuery:$ ...
- Enterprise Solution 2.3
1. 登陆窗体和主界面增加语言选项,同时可记住用户登陆的语言和数据库. 2. 主界面的树功能可记住上次打开的模块菜单. 3. 修复主界面菜单生成问题和导航图区上下文菜单生成问题. 4. 增加自动更新功 ...
- 提高 DHTML 页面性能
联盟电脑摘要:本文说明了某些DHTML功能对性能的重大影响,并提供了一些提高DHTML页面性能的技巧. 目录 简介 成批处理DHTML更改 使用innerText 使用DOM添加单个元素 扩展SELE ...
- Geometry Surface of OpenCascade BRep
Geometry Surface of OpenCascade BRep eryar@163.com 摘要Abstract:几何曲面是参数表示的曲面 ,在边界表示中其数据存在于BRep_TFace中, ...
- SQLExecption:Operation not allowed after ResultSet closed解决办法
原网址:http://blog.csdn.net/sku0923/article/details/1722370 一个stmt多个rs进行操作引起的ResultSet已经关闭错误 一个stmt多个rs ...
- java.util.Scanner简单应用
import java.util.Scanner; import java.io.*; public class FileScannerTest{ public static void main(St ...