前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Scalaz(41)- Free :IO Monad-Free特定版本的FP语法

Scalaz(41)- Free :IO Monad-Free特定版本的FP语法

作者头像
用户1150956
发布于 2018-01-05 02:34:44
发布于 2018-01-05 02:34:44
1.6K00
代码可运行
举报
运行总次数:0
代码可运行

我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码。像通过键盘显示器进行交流、读写文件、数据库等这些IO操作都会产生副作用。那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段烧CPU的代码,没有任何意义,所以任何类型的程序都必须具备IO功能,而在FP模式中对IO操作有特别的控制方式:具体实现是通过把代码中产生副作用的部分抽离出来延后运算(在所有纯代码运算之后)。scalaz的IO Monad就是处理副作用代码延后运算的一种数据结构。我先举个简单的例子来示范如何通过一种数据结构来实现对副作用代码的延迟运算:人机交互是一种典型的IO,有键盘输入,又有显示屏输出。println,readLine都会产生副作用,我们必须用一种数据类型来实现副作用代码抽离及延后运算,这种类型就是IO。我们先看看这个例子:我们希望实现人机交互如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 def ask(prompt: String): String = { 
2    println(prompt)
3    readLine
4  }
5 def tell(msg: String): Unit = println(msg)
6 for {
7   name <- ask("what's your name?")
8   _ <- tell(s"I'm $name")
9 } yield()

ask和tell分别返回String和Unit,它们都是副作用即时产生的结果。ask和tell都是非纯函数。我们可以设计一个类型来实现副作用代码抽离:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 trait MyIO[+A] {self =>
 2   def run: A
 3   def map[B](f: A => B): MyIO[B] =
 4     new MyIO[B] {
 5        def run = f(self.run)
 6     }
 7   def flatMap[B](f: A => MyIO[B]): MyIO[B] =
 8     new MyIO[B] {
 9        def run = f(self.run).run
10     }
11 }
12 object MyIO {
13   def apply[A](a: A) = new MyIO[A] { def run = a }
14   implicit val ioMonad = new Monad[MyIO] {
15      def point[A](a: => A) = new MyIO[A] { def run = a }
16      def bind[A,B](ma: MyIO[A])(f: A => MyIO[B]): MyIO[B] =
17        ma flatMap f
18   }
19 }

现在我们可以把ask和tell函数的返回类型改成MyIO:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 import MyIO._
 2 def ask(prompt: String): MyIO[String] = 
 3   MyIO {
 4        println(prompt)
 5        readLine
 6   }
 7 def tell(msg: String): MyIO[Unit] =
 8   MyIO {
 9     println(msg)
10   }

MyIO是个Monad,我们可以在for-comprehension里用行令方式编程了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 val org: MyIO[Unit] = for {
2   first <- ask("What's your first name?")
3   last <- ask("What's your last name?")
4   _ <- tell(s"Hello $first $last!")
5 } yield()

注意,在这个阶段我们只完成了对一个程序功能的描述,实际运算是在MyIO.run那里:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 object MyIOApp extends App {
 2   import MyIOFunctions._
 3   pr.run
 4 }
 5 //运算结果:
 6 What's your first name?
 7 Tiger
 8 What's your last name?
 9 Chan
10 Hello Tiger Chan!

run是MyIO类型的interpreter。现在我们已经实现了程序描述(算式)和运算(算法)的关注分离。而且我们可以随便使用ask和tell而不进行运算,延迟至调用run对MyIO类型进行运算。以上只是一种简单的示范IO类型,我先把完整的源代码提供如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 package demo.app
 2 import scalaz._
 3 import Scalaz._
 4 
 5 trait MyIO[+A] {self =>
 6   def run: A
 7   def map[B](f: A => B): MyIO[B] =
 8     new MyIO[B] {
 9        def run = f(self.run)
10     }
11   def flatMap[B](f: A => MyIO[B]): MyIO[B] =
12     new MyIO[B] {
13        def run = f(self.run).run
14     }
15 }
16 object MyIO {
17   def apply[A](a: A) = new MyIO[A] { def run = a }
18   implicit val ioMonad = new Monad[MyIO] {
19      def point[A](a: => A) = new MyIO[A] { def run = a }
20      def bind[A,B](ma: MyIO[A])(f: A => MyIO[B]): MyIO[B] =
21        ma flatMap f
22   }
23 }
24 object MyIOFunctions {
25 import MyIO._
26 def ask(prompt: String): MyIO[String] = 
27   MyIO {
28        println(prompt)
29        readLine
30   }
31 def tell(msg: String): MyIO[Unit] =
32   MyIO {
33     println(msg)
34   }
35 val prg: MyIO[Unit] = for {
36   first <- ask("What's your first name?")
37   last <- ask("What's your last name?")
38   _ <- tell(s"Hello $first $last!")
39 } yield()
40 
41 
42 }
43 object MyIOApp extends App {
44   import MyIOFunctions._
45   prg.run
46 }

scalaz的IO Monad当然复杂的多。我们看看scalaz的IO Monad是怎样的:effect/IO.scala

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sealed abstract class IO[A] {
  private[effect] def apply(rw: Tower[IvoryTower]): Trampoline[(Tower[IvoryTower], A)]
...
/** Continues this action with the given function. */
  def map[B](f: A => B): IO[B] = io(rw =>
    apply(rw) map {
      case (nw, a) => (nw, f(a))
    })

  /** Continues this action with the given action. */
  def flatMap[B](f: A => IO[B]): IO[B] = io(rw =>
    apply(rw) flatMap {
      case (nw, a) => f(a)(nw)
    })
...
 /** Construct an IO action from a world-transition function. */
  def io[A](f: Tower[IvoryTower] => Trampoline[(Tower[IvoryTower], A)]): IO[A] =
    new IO[A] {
      private[effect] def apply(rw: Tower[IvoryTower]) = Free(() => f(rw))
    }

可以看得出io[A](...)就是IO的构建器(constructor)。IO[A]类型是Free[Function0,A] = Free(() => f(rw)),最终就是一个Trampoline,这个可以从下面的apply看得出来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
object IO extends IOInstances {
  def apply[A](a: => A): IO[A] =
    io(rw => return_(rw -> a))
...

上面的Tower[IvoryTower]是状态切换函数的输入类型,不参与实际运算(不在任何运算中调用如: rw -> a),起一种状态标签作用(state tag)。主要提供给编译器(compiler)做类型安全用。实际上这个状态切换函数就是一个延迟运算值 => A。io把这个值挂到Free的suspend状态:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** Suspend the given computation in a single step. */
  def return_[S[_], A](value: => A)(implicit S: Applicative[S]): Free[S, A] =
    liftF[S, A](S.point(value))
代码语言:javascript
代码运行次数:0
运行
复制

再看看IO的运算方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sealed abstract class IO[A] {
  private[effect] def apply(rw: Tower[IvoryTower]): Trampoline[(Tower[IvoryTower], A)]

  import IO._

  /**
   * Runs I/O and performs side-effects. An unsafe operation.
   * Do not call until the end of the universe.
   */
  def unsafePerformIO(): A = apply(ivoryTower).run._2

先用apply建Trampoline,再运行Free.run(Trampoline[A]=Free[Function0,A])。注意,我们并没有采用这个Tower[IvoryTower]。再者,函数unsafePerformIO是通过private函数apply先构建了Trampoline后再进行运算的。换言之IO Monad的用户是无法自定义算法(interpreter)的。我们前面曾经把Free描述成可以自定义F[A]编程语言的数据结构,那么IO[A]就是一种固定的FP编程语言,它只有unsafePerformIO一种算法(interpreter)。

IO Monad可以使我们更方便地在IO这个壳子里进行我们熟悉的行令编程(imperative programming),因为我们只需要把行令程序直接放进IO里就行了。看看下面这些例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 val hello = print("hello ").point[IO]             //> hello  : scalaz.effect.IO[Unit] = scalaz.effect.IO$$anon$6@145eaa29
2 val world = IO (print("world,"))                  //> world  : scalaz.effect.IO[Unit] = scalaz.effect.IO$$anon$6@57c758ac
3 val howareyou = io {rw => return_(rw -> println("how are you!"))}
4                                                   //> howareyou  : scalaz.effect.IO[Unit] = scalaz.effect.IO$$anon$6@a9cd3b1
5 val greeting = hello |+| world |+| howareyou      //> greeting  : scalaz.effect.IO[Unit] = scalaz.effect.IO$$anon$6@481a996b
6 greeting.unsafePerformIO                          //> hello world,how are you!

这个例子示范了用三种方式把副作用语句print升格成IO。不要被IO[A]的IO字面误导了,IO[A]的这个A不一定是副作用命令,任何行令编程使用的语句都可以放人IO[_],包括变量申明、赋值、文件读写等。所以我们说IO Monad就是在FP模式中进行行令编程的通用方式。可以想象我们可能会在IO这个壳子内进行我们熟悉的程序编写。那么IO Monad到底能不能符合在FP环境内的行令编程要求呢?我们可以用几个例子来对流程控制(flow control),跟踪记录(logging)即异常处理(exception handling)等方面进行测试示范:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 import scalaz._
 2 import Scalaz._
 3 import effect._
 4 import IO._
 5 import Free._
 6 import scala.language.higherKinds
 7 import scala.language.implicitConversions
 8 
 9 object IOPrg {
10   def div(dvdn: Int, dvsor: Int): IO[Int] = 
11     IO(dvdn / dvsor)
12   val ioprg: IO[Int] = for {
13     _ <- putLn("enter dividend:")
14     dvdn <- readLn
15     _ <- putLn("enter divisor:")
16     dvsor <- readLn
17     quot <- div(dvdn.toInt, dvsor.toInt)
18     _ <- putLn(s"the result:$quot")
19   } yield quot
20 }
21 
22 object IOMonadDemo extends App {
23   import IOPrg._
24   ioprg.unsafePerformIO()
25 }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"enter dividend:"
10
"enter divisor:"
5
"the result:2"

ioprg是一段包含了带副作用语句的程序。所有副作用延迟到unsafePerformIO()才正真产生。现在我们先示范流程控制,试着提早跳出这个for-loop。这不就是option在for-comprehension的作用吗。我们需要在IO[A]这种Monad之上增加Option的作用,可以用Monad Transformer来实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1   implicit def ioToOptionT[A](io: IO[A]): OptionT[IO,A] = io.liftM[OptionT]
 2   val optionIOprg: OptionT[IO,Int] = for {
 3     _ <- putLn("enter dividend:")
 4     dvdn <- readLn
 5     _ <- putLn("enter divisor:")
 6     dvsor <- readLn
 7     quot <- div(dvdn.toInt, dvsor.toInt)
 8     _ <- putLn(s"the result:$quot")
 9   } yield quit
10 ...
11 object IOMonadDemo extends App {
12   import IOPrg._
13 //  ioprg.unsafePerformIO()
14   optionIOprg.run.unsafePerformIO()
15 }
16 ...
17 "enter dividend:"
18 10
19 "enter divisor:"
20 5
21 "the result:2"

我们把结果类型变成Option[IO,Int]后并没有改变程序的功能。现在试试中途终止:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1   val optionIOprg: OptionT[IO,Int] = for {
 2     _ <- putLn("enter dividend:").liftM[OptionT]
 3     dvdn <- readLn.liftM[OptionT]
 4     _ <- putLn("enter divisor:").liftM[OptionT]
 5     dvsor <- readLn.liftM[OptionT]
 6     a <- if (dvsor.toInt == 0 ) OptionT(IO(None: Option[String])) else IO(0).liftM[OptionT]
 7     quot <- div(dvdn.toInt, dvsor.toInt).liftM[OptionT]
 8     _ <- putLn(s"the result:$quot").liftM[OptionT]
 9   } yield quit
10 ...
11 "enter dividend:"
12 10
13 "enter divisor:"
14 5
15 "the result:2"
16 
17 Process finished with exit code 0
18 ...
19 "enter dividend:"
20 10
21 "enter divisor:"
22 0
23 
24 Process finished with exit code 0

不错,的确在录入0时可以中途退出。不过现在for-comprehension里的每句都需要用liftM[OptionT]来升格。无法像前面统统用implicit def ioToOptionT来自动升格,这是因为把None升格时会产生类型推导问题,这个就放在以后再研究了。

同样如果我们希望把用户的输入记录下来,我们可以用Writer的功能来实现。下一个例子就是Writer-IO Monad Transformer示范了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1   type WriterTIO[F[_],A] = WriterT[F,List[String],A]
 2   val writerIOprg: WriterT[IO,List[String],Int] = for {
 3     _ <- putLn("enter dividend:").liftM[WriterTIO]
 4     dvdn <- readLn.liftM[WriterTIO]
 5     _ <- WriterT.writerT((List(s"received dividend $dvdn"),dvdn).point[IO])
 6     _ <- putLn("enter divisor:").liftM[WriterTIO]
 7     dvsor <- readLn.liftM[WriterTIO]
 8     _ <- WriterT.writerT(IO(List(s"received divisor $dvsor, ready to divide ..."),dvdn))
 9     quot <- div(dvdn.toInt, dvsor.toInt).liftM[WriterTIO]
10     _ <- putLn(s"the result:$quot").liftM[WriterTIO]
11   } yield quit
12 ...
13 object IOMonadDemo extends App {
14   import IOMonadPrg._
15   //  ioprg.unsafePerformIO()
16   //optionIOprg.run.unsafePerformIO()
17   println(writerIOprg.run.unsafePerformIO())
18 }
19 ...
20 "enter dividend:"
21 10
22 "enter divisor:"
23 5
24 "the result:2"
25 (List(received dividend 10, received divisor 5, ready to divide ...),2)
26 
27 Process finished with exit code 0

用WriterT可以达到logging目的。当然,我们可以同时拥有Option和Writer的作用,这时的Monad Transformer就是三层的了,我们在前面的这篇讨论也做过示范。

最后看个异常处理示范:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1   type WriterTIO[F[_],A] = WriterT[F,List[String],A]
 2   val writerIOprg: WriterT[IO,List[String],Int] = for {
 3     _ <- putLn("enter dividend:").liftM[WriterTIO]
 4     dvdn <- readLn.liftM[WriterTIO]
 5     _ <- WriterT.writerT((List(s"received dividend $dvdn;"),dvdn).point[IO])
 6     _ <- putLn("enter divisor:").liftM[WriterTIO]
 7     dvsor <- readLn.liftM[WriterTIO]
 8     _ <- WriterT.writerT(IO(List(s"received divisor $dvsor, ready to divide ..."),dvdn))
 9     quot <- div(dvdn.toInt, dvsor.toInt).except(e => IO({println(e.getMessage());-99})).liftM[WriterTIO]
10     _ <- if (quot < 0) WriterT.writerT((List(s"divide by zero Error!!!"),-99).point[IO]) else putLn(s"the result:$quot").liftM[WriterTIO]
11   } yield (quot)
12 ...
13 object IOMonadDemo extends App {
14   import IOMonadPrg._
15   //  ioprg.unsafePerformIO()
16   //optionIOprg.run.unsafePerformIO()
17   println(writerIOprg.run.unsafePerformIO())
18 ...
19 "enter dividend:"
20 3
21 "enter divisor:"
22 0
23 / by zero
24 (List(received dividend 3;, received divisor 0, ready to divide ..., divide by zero Error!!!),-99)
25 
26 Process finished with exit code 0

以上例子调用了scalaz IO Monad typeclass 提供的except方法,scalaz还提供了其它的异常处理函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /** Executes the handler if an exception is raised. */
  def except(handler: Throwable => IO[A]): IO[A] =
    io(rw => try { Free.pure(this(rw).run) } catch { case e: Throwable => handler(e)(rw) })

  /**
   * Executes the handler for exceptions that are raised and match the given predicate.
   * Other exceptions are rethrown.
   */
  def catchSome[B](p: Throwable => Option[B], handler: B => IO[A]): IO[A] =
    except(e => p(e) match {
      case Some(z) => handler(z)
      case None => throw e
    })

  /**
   * Returns a disjunction result which is right if no exception was raised, or left if an
   * exception was raised.
   */
  def catchLeft: IO[Throwable \/ A] =
    map(\/.right[Throwable, A]) except (t => IO(-\/(t)))

  /**Like "catchLeft" but takes a predicate to select which exceptions are caught. */
  def catchSomeLeft[B](p: Throwable => Option[B]): IO[B \/ A] =
    catchLeft map (_.leftMap(e => p(e).getOrElse(throw e)))

  /**Like "finally", but only performs the final action if there was an exception. */
  def onException[B](action: IO[B]): IO[A] = this except (e => for {
    _ <- action
    a <- (throw e): IO[A]
  } yield a)

以下是这次讨论的完整示范源代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 package demo.app
 2 
 3 import scalaz._
 4 import Scalaz._
 5 import effect._
 6 import IO._
 7 import Free._
 8 import scala.language.higherKinds
 9 import scala.language.implicitConversions
10 
11 object IOMonadPrg {
12   def div(dvdn: Int, dvsor: Int): IO[Int] =
13     IO(dvdn / dvsor)
14   val ioprg: IO[Int] = for {
15     _ <- putLn("enter dividend:")
16     dvdn <- readLn
17     _ <- putLn("enter divisor:")
18     dvsor <- readLn
19     quot <- div(dvdn.toInt, dvsor.toInt)
20     _ <- putLn(s"the result:$quot")
21   } yield quot
22   //implicit def ioToOptionT[A](io: IO[A]): OptionT[IO,A] = io.liftM[OptionT]
23   val optionIOprg: OptionT[IO,Int] = for {
24     _ <- putLn("enter dividend:").liftM[OptionT]
25     dvdn <- readLn.liftM[OptionT]
26     _ <- putLn("enter divisor:").liftM[OptionT]
27     dvsor <- readLn.liftM[OptionT]
28     a <- if (dvsor.toInt == 0 ) OptionT(IO(None: Option[String])) else IO(0).liftM[OptionT]
29     quot <- div(dvdn.toInt, dvsor.toInt).liftM[OptionT]
30     _ <- putLn(s"the result:$quot").liftM[OptionT]
31   } yield quot
32   type WriterTIO[F[_],A] = WriterT[F,List[String],A]
33   val writerIOprg: WriterT[IO,List[String],Int] = for {
34     _ <- putLn("enter dividend:").liftM[WriterTIO]
35     dvdn <- readLn.liftM[WriterTIO]
36     _ <- WriterT.writerT((List(s"received dividend $dvdn;"),dvdn).point[IO])
37     _ <- putLn("enter divisor:").liftM[WriterTIO]
38     dvsor <- readLn.liftM[WriterTIO]
39     _ <- WriterT.writerT(IO(List(s"received divisor $dvsor, ready to divide ..."),dvdn))
40     quot <- div(dvdn.toInt, dvsor.toInt).except(e => IO({println(e.getMessage());-99})).liftM[WriterTIO]
41     _ <- if (quot < 0) WriterT.writerT((List(s"divide by zero Error!!!"),-99).point[IO]) else putLn(s"the result:$quot").liftM[WriterTIO]
42   } yield (quot)
43 
44 
45 }
46 
47 object IOMonadDemo extends App {
48   import IOMonadPrg._
49   //  ioprg.unsafePerformIO()
50   //optionIOprg.run.unsafePerformIO()
51   println(writerIOprg.run.unsafePerformIO())
52 }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-05-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Scalaz(12)- Monad:再述述flatMap,顺便了解MonadPlus
  在前面的几篇讨论里我们初步对FP有了些少了解:FP嘛,不就是F[A]吗?也是,FP就是在F[]壳子(context)内对程序的状态进行更改,也就是在F壳子(context)内施用一些函数。再直白一
用户1150956
2018/01/05
9560
Scalaz(28)- ST Monad :FP方式适用变量
    函数式编程模式强调纯代码(pure code),主要实现方式是使用不可变数据结构,目的是函数组合(composability)最终实现函数组件的重复使用。但是,如果我们在一个函数p内部使用了可
用户1150956
2018/01/05
5700
Scalaz(10)- Monad:就是一种函数式编程模式-a design pattern
    Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming
用户1150956
2018/01/05
7910
Scalaz(54)- scalaz-stream: 函数式多线程编程模式-Free Streaming Programming Model
该文介绍了如何使用Free Monad和Scalaz-Stream实现一个功能类似于命令行交互的程序。通过Free Monad和Scalaz-Stream,可以编写异步代码,实现多线程和分布式计算,同时避免使用回调函数和事件循环。该示例使用了Scalaz的Task和Process,以及Apache Arrow的Array和Record,通过这些工具,可以高效地处理大量数据。
用户1150956
2018/01/05
6030
Scalaz(13)- Monad:Writer - some kind of logger
  通过前面的几篇讨论我们了解到F[T]就是FP中运算的表达形式(representation of computation)。在这里F[]不仅仅是一种高阶类型,它还代表了一种运算协议(computa
用户1150956
2018/01/05
9240
Scalaz(17)- Monad:泛函状态类型-State Monad
  我们经常提到函数式编程就是F[T]。这个F可以被视为一种运算模式。我们是在F运算模式的壳子内对T进行计算。理论上来讲,函数式程序的运行状态也应该是在这个运算模式壳子内的,也是在F[]内更新的。那么
用户1150956
2018/01/05
1.8K0
Scalaz(39)- Free :a real monadic program
本文介绍了什么是RESTful API,以及其在软件开发中的重要性。RESTful API是一种基于HTTP协议的网络应用程序接口,它遵循一定的约束和规范,使得开发人员能够更加容易和高效地构建和部署API。RESTful API具有可扩展性、可维护性和可重用性的特点,因此被广泛应用于各种软件开发场景中。
用户1150956
2018/01/05
1.3K0
Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言
  说道FP,我们马上会联想到Monad。我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串型的流程(workflow)。更直白的讲
用户1150956
2018/01/05
1.5K0
Scalaz(11)- Monad:你存在的意义
    前面提到了scalaz是个函数式编程(FP)工具库。它提供了许多新的数据类型、拓展的标准类型及完整的一套typeclass来支持scala语言的函数式编程模式。我们知道:对于任何类型,我们只需
用户1150956
2018/01/05
9170
Scalaz(32)- Free :lift - Monad生产线
    在前面的讨论里我们提到自由数据结构就是产生某种类型的最简化结构,比如:free monoid, free monad, free category等等。我们也证明了List[A]是个free
用户1150956
2018/01/05
5010
Scalaz(43)- 总结 :FP就是实用的编程模式
本文探讨了函数式编程在软件工程领域中的应用,包括函数式编程的优缺点,与面向对象编程的区别,以及如何在实践中应用函数式编程。作者还探讨了函数式编程和形式方法的关系,并介绍了一些函数式编程的工具和语言。
用户1150956
2018/01/05
1.1K0
Scalaz(36)- Free :实践-Free In Action - 实用体验
在上面几期讨论中我们连续介绍了Free Monad。因为FP是纯函数编程,也既是纯函数的组合集成,要求把纯代码和副作用代码可以分离开来。Free Monad的程序描述(AST)和程序实现(Inte
用户1150956
2018/01/05
9410
Scalaz(33)- Free :算式-Monadic Programming
该文是关于Scala实现函数式编程的概述。通过使用Scalaz库,可以定义类型安全的函数式编程抽象。通过使用这些抽象,可以避免“副作用”并实现依赖编程。
用户1150956
2018/01/05
5500
Scalaz(34)- Free :算法-Interpretation
我们说过自由数据结构(free structures)是表达数据类型的最简单结构。List[A]是个数据结构,它是生成A类型Monoid的最简单结构,因为我们可以用List的状态cons和Nil来分
用户1150956
2018/01/05
7620
泛函编程(31)-泛函IO:Free Monad-Running free
  在上节我们介绍了Free Monad的基本情况。可以说Free Monad又是一个以数据结构替换程序堆栈的实例。实际上Free Monad的功能绝对不止如此,以heap换stack必须成为Fr
用户1150956
2018/01/05
1.2K0
Scalaz(53)- scalaz-stream: 程序运算器-application scenario
    从上面多篇的讨论中我们了解到scalaz-stream代表一串连续无穷的数据或者程序。对这个数据流的处理过程就是一个状态机器(state machine)的状态转变过程。这种模式与我们通常遇到
用户1150956
2018/01/05
5640
Scalaz(22)- 泛函编程思维: Coerce Monadic Thinking
用户1150956
2018/01/05
5150
Scalaz(38)- Free :Coproduct-Monadic语句组合
该文摘要总结:本文主要介绍了如何通过 FreeBSD 的 copro多态性来编写一个跨平台的应用程序。作者通过一个简单的示例,展示了如何使用 FreeBSD 的 copro多态性来编写一个跨平台的应用程序,并探讨了 FreeBSD 的 copro多态性的实现原理和优缺点。
用户1150956
2018/01/05
1.1K0
Scalaz(47)- scalaz-stream: 深入了解-Source
   scalaz-stream库的主要设计目标是实现函数式的I/O编程(functional I/O)。这样用户就能使用功能单一的基础I/O函数组合成为功能完整的I/O程序。还有一个目标就是保证资源
用户1150956
2018/01/05
7570
Scalaz(49)- scalaz-stream: 深入了解-Sink/Channel
   一个完整的scalaz-stream有以下几个部分组成:Source -> Transducer -> Sink,用直白文字来描述就是:“输入 -> 传换 -> 输出”。我们已经在前面几篇讨论中
用户1150956
2018/01/05
5890
推荐阅读
相关推荐
Scalaz(12)- Monad:再述述flatMap,顺便了解MonadPlus
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验