在前面的讨论里我们提到自由数据结构就是产生某种类型的最简化结构,比如: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]。我们用个例子来证明:

 1 package Exercises
 2 import scalaz._
 3 import Scalaz._
 4 import scala.language.higherKinds
 5 import scala.language.implicitConversions
 6 object freelift {
 7 trait Config[+A] {
 8   def get: A
 9 }
10 object Config {
11   def apply[A](a: A): Config[A] = new Config[A] { def get = a}
12   implicit val configFunctor = new Functor[Config] {
13      def map[A,B](ca: Config[A])(f: A => B) = Config[B](f(ca.get))
14   }
15 }
16
17 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]:

1 trait Interact[+A]  //Console交互
2 //println(prompt: String) then readLine 返回String
3 case class Ask(prompt: String) extends Interact[String]
4 //println(msg: String) 不反回任何值
5 case class Tell(msg: String) extends Interact[Unit]

由于Ask和Tell都不会返回泛类值,所以无需或者无法实现map函数,Interact[A]是个不是Functor的高阶类。我们必须把它转成Coyoneda提供给Free产生Monad:

1 Free.liftFC(Tell("hello"))                        //> res0: scalaz.Free.FreeC[Exercises.freelift.Interact,Unit] = Suspend(scalaz.Coyoneda$$anon$22@71423665)
2 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 = A0def TC = TC0def 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 = B0def TC = TC0def leibniz = refl}

好了。但是又想了想,一个是Functor的Interact又是怎样的呢?那Ask必须返回一个值,而这个值应该是个Free,实际上是代表下一个运算:

 1 package Exercises
 2 import scalaz._
 3 import Scalaz._
 4 import scala.language.higherKinds
 5 object interact {
 6 trait Interact[+A]
 7 case class Ask[Next](prompt: String, n: String => Next) extends Interact[Next]
 8 case class Tell[Next](msg: String, n: Next) extends Interact[Next]
 9
10 object interFunctor extends Functor[Interact] {
11   def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ia match {
12       case Tell(m,n) => Tell(m, f(n))
13       case g: Ask[A] => Ask[B](g.prompt, g.n andThen f)
14   }
15 }

所以Ask返回Next类型值,应该是个Free,代表下一个运算。

转载于:https://www.cnblogs.com/tiger-xc/p/5290667.html

Scalaz(32)- Free :lift - Monad生产线相关推荐

  1. scalaz使用_日常使用的Scalaz功能第2部分:Monad变形金刚和Reader Monad

    scalaz使用 在" Scalaz日常使用功能"的第二篇文章中,我们将探讨Monad变压器和Reader monad的主题.让我们从Monad变压器开始. 当您不得不处理嵌套的M ...

  2. 日常使用的Scalaz功能第2部分:Monad变形金刚和Reader Monad

    在" Scalaz日常使用功能"的第二篇文章中,我们将探讨Monad变压器和Reader monad的主题.让我们从Monad变压器开始. 当您不得不处理嵌套的Monad时,Mon ...

  3. Java社区目前的现状——交易

    这是关于一笔交易的故事. 没有人为交易签过字. 但这仍然是一笔重要的交易. 这是Java的主人和Java社区之间的交易. 交易 这是我对Java的主人和Java社区之间如何相互影响作用的观点: Jav ...

  4. 可伸缩性, 可用性和稳定性模式 Scalability, Availability Stability Patterns

    Scalability, Availability & Stability Patterns 一 自我有要求的读者应该提出问题:(研习:掌握层次:)能力级别:不会(了解)--领会(理解)--熟 ...

  5. ​Hi wiki主客观双数据用户增长实践

    作者:刘顺炬 Google Analytics 认证分析师 本文为「人人都是产品经理」社区和友盟+联合举办的"2019「友盟杯」数据分析大赛"中获奖作品.说明:文中数据已进行脱敏处 ...

  6. Scalaz(25)- Monad: Monad Transformer-叠加Monad效果

    中间插播了几篇scalaz数据类型,现在又要回到Monad专题.因为FP的特征就是Monad式编程(Monadic programming),所以必须充分理解认识Monad.熟练掌握Monad运用.曾 ...

  7. Scalaz(12)- Monad:再述述flatMap,顺便了解MonadPlus

    在前面的几篇讨论里我们初步对FP有了些少了解:FP嘛,不就是F[A]吗?也是,FP就是在F[]壳子(context)内对程序的状态进行更改,也就是在F壳子(context)内施用一些函数.再直白一点就 ...

  8. Scalaz(38)- Free :Coproduct-Monadic语句组合

    很多函数式编程爱好者都把FP称为Monadic Programming,意思是用Monad进行编程.我想FP作为一种比较成熟的编程模式,应该有一套比较规范的操作模式吧.因为Free能把任何F[A]升格 ...

  9. Scalaz(44)- concurrency :scalaz Future,尚不完整的多线程类型

    scala已经配备了自身的Future类.我们先举个例子来了解scala Future的具体操作: 1 import scala.concurrent._ 2 import ExecutionCont ...

最新文章

  1. Spring Boot + Vue 如此强大?
  2. android h5状态栏消息,安卓实现系统状态栏的隐藏和显示
  3. redis详解(四)-- 高可用分布式集群
  4. 在JavaScript中以Hours24:Minutes:Seconds格式获取当前时间
  5. 用python做频数分析_使用Python进行描述性统计
  6. 深夜,在这个已不再喧嚣的城市中寻找到一片属于自己的宁静,仰望那片属于自己的星空……...
  7. 安卓文件操作全解:内部文件、公共文件、私有文件、app静态文件
  8. 【批处理DOS-CMD命令-汇总和小结】-输出/显示命令——echo
  9. mybatis(动态sql、结果集处理、分页、特殊字符处理)
  10. 自研返利网源码、有需要的私信
  11. LOD原理及相关实现方式
  12. 前端开发练习——包含了计时功能的动画时钟
  13. CF1139C Edgy TreesDFS求连通块大小、思维
  14. su切换为root并输入root密码确定正确但会有卡顿并提示su: Permission denied
  15. java 边读边写文件_文件操作--边读边写,在指定位置添加内容(避免乱码)
  16. unity 鼠标放置 ui_ui层次结构以及不常见但至关重要的任务放置在哪里
  17. Setup time 和 Hold time
  18. Autofac依赖注入
  19. MarkDown渲染无法显示
  20. 公链分析报告(6)--Zcash

热门文章

  1. smarty模板引擎_6-Smarty的内置函数
  2. Android源码分析-全面理解Context
  3. 用Python操作Oracle
  4. 《Java解惑》陷阱和缺陷的目录
  5. golang常用技巧
  6. 面试碰到这个算法:字母异位词分组
  7. git reset --hard HEAD
  8. nagle算法和延迟ACK
  9. 枚举类型enum需要注意的6点!
  10. python三十六:shelve模块