## 动力学知识库

由于泛函编程非常重视函数组合（function composition），任何带有副作用（side effect）的函数都无法实现函数组合，所以必须把包含外界影响（effectful）副作用不纯代码（impure code）函数中的纯代码部分（pure code）抽离出来形成独立的另一个纯函数。我们通过代码抽离把不纯代码逐步抽离向外推并在程序里形成一个纯代码核心（pure core）。这样我们就可以顺利地在这个纯代码核心中实现函数组合。IO Monad就是泛函编程处理副作用代码的一种手段。我们先用个例子来示范副作用抽离：

`1 case class Player(name: String, score: Int)2 def printWinner(p: Player): Unit =3 println(p.name + " is the winner!")4 def declareWinner(p1: Player, p2: Player): Unit =5 if (p1.score > p2.score ) printWinner(p1)6 else printWinner(p2)`

`1 def printWinner(p: Player): Unit =2 println(p.name + " is the winner!")3 def winner(p1: Player, p2: Player): Player =4 if (p1.score > p2.score) p15 else p26 def declareWinner(p1: Player, p2: Player): Unit =7 printWinner(winner(p1, p2))`

`1 def winnerMsg(p: Player): String =2 p.name + " is the winner!"3 def printWinner(p: Player): Unit =4  println(winnerMsg(p))5 def winner(p1: Player, p2: Player): Player =6 if (p1.score > p2.score) p17 else p28 def declareWinner(p1: Player, p2: Player): Unit =9 printWinner(winner(p1, p2))`

1、一个纯函数：A => D, 这里D只是一个功能描述表达式

2、一个带副作用的非纯函数： D => B, 它可以被视为D的解译器（interpreter）,把描述解译成有副作用的指令

` 1 trait IO {def run: Unit } 2 def printLine(line: String) : IO = new IO { 3 def run = println(line) 4 } 5 def printWinner(p: Player): IO = 6  printLine(winnerMsg(p)) 7 case class Player(name: String, score: Int) 8 def winnerMsg(p: Player): String = 9 p.name + " is the winner!"10 def winner(p1: Player, p2: Player): Player =11 if (p1.score > p2.score) p112 else p213 def declareWinner(p1: Player, p2: Player): Unit =14 printWinner(winner(p1, p2))`

`1 trait IO[+A] { self =>2  def run: A3 def map[B](f: A => B): IO[B] =4 new IO[B] { def run = f(self.run)}5 def flatMap[B](f: A => IO[B]): IO[B] =6 new IO[B] {def run = f(self.run).run}7 }`

`1 object IO extends Monad[IO] {2 def unit[A](a: A) = new IO[A] {def run = a}3 def flatMap[A,B](ma: IO[A])(f: A => IO[B]) = ma flatMap f4 def map[A,B](ma: IO[A])(f: A => B) = ma map f5 def apply[A](a: A) = unit(a) //IO构建器，可以实现 IO {...}6 }`

`1 def ReadLine: IO[String] = IO { readLine }2 def PrintLine(msg: String): IO[Unit] = IO { println(msg) }3 def fahrenheitToCelsius(f: Double): Double =4 (f -32) * 5.0 / 9.05 def converter: IO[Unit] = for {6 _ <- PrintLine("Enter a temperature in degrees fahrenheit:")7 d <- ReadLine.map(_.toDouble)8 _ <- PrintLine(fahrenheitToCelsius(d).toString)9 } yield ()`

`1 trait IO[A] {def run: A}2 case class Pure[+A](a: A) extends IO[A]3 case class Request[Extenal[_],I,A](expr: Extenal[I], cont: I => IO[A]) extends IO[A]`

`1 trait Runnable[A] { def run: A }2 object Delay {3 def apply[A](a: A) = new Runnable[A] { def run = a}4 }5 Delay {println("SIDE EFFECTS!!!")}`

`1 trait IO[F[_],+A] {}2 case class Pure[F[_],+A](get: A) extends IO[F,A]3 case class Request[F[_],I,+A](expr: F[I], cont: I => IO[F,A]) extends IO[F,A]`

`1 trait Console[A]2 case object ReadLine extends Console[Option[String]]3 case class PrintLine(msg: String) extends Console[Unit]`

` 1 trait Run[F[_]] { 2  def apply[A](expr: F[A]): (A, Run[F]) 3 } 4 object IO { 5 def run[F[_],A](R: Run[F])(io: IO[F,A]): A = io match { 6 case Pure(a) => a 7 case Request(expr, cont) => R(expr) match { 8 case (a,r2) => run(r2)(cont(a)) 9  }10  }11 }`

` 1 trait Console[A] 2 case object ReadLine extends Console[Option[String]] 3 case class PrintLine(msg: String) extends Console[Unit] 4 5 object RunConsole extends Run[Console] { 6 def apply[A](c: Console[A]): (A, Run[Console]) = c match { 7 case ReadLine => { 8 val r = try Some(readLine) catch { case _ => None } 9  (r, RunConsole)10  }11 case PrintLine(m) => (println(m),RunConsole)12  }13 }14 IO.run(RunConsole)(ioprg)`

` 1 trait IO[F[_],A] { 2 def unit(a: A) = Pure(a) 3 def flatMap[B](f: A => IO[F,B]): IO[F,B] = this match { 4 case Pure(a) => f(a) 5 // case Request(expr,cont) => Request(expr, cont andThen (_ flatMap f)) 6 case Request(expr,cont) => Request(expr, (x: Any) => cont(x) flatMap f) 7  } 8 def map[B](f: A => B): IO[F,B] = flatMap(a => Pure(f(a))) 9 }10 case class Pure[F[_],A](get: A) extends IO[F,A]11 case class Request[F[_],I,A](expr: F[I], cont: I => IO[F,A]) extends IO[F,A]`

`1 def ioMonad[F[_]] = new Monad[({type l[x] = IO[F, x]})#l] {2 def unit[A](a: A) = Pure(a)3 def flatMap[A,B](fa:IO[F,A])(f: A => IO[F,B]): IO[F,B] = fa flatMap f4 def map[A,B](fa: IO[F,A])(f: A => B): IO[F,B] = fa map f56 }`

` 1 trait IO[F[_],A] { 2 def unit(a: A) = Pure(a) 3 def flatMap[B](f: A => IO[F,B]): IO[F,B] = this match { 4 case Pure(a) => f(a) 5 // case Request(expr,cont) => Request(expr, cont andThen (_ flatMap f)) 6 case Request(expr,cont) => Request(expr, (x: Any) => cont(x) flatMap f) 7  } 8 def map[B](f: A => B): IO[F,B] = flatMap(a => Pure(f(a))) 9 def runM[F[_],A](F: Monad[F])(io: IO[F,A]): F[A] = io match {10 case Pure(a) => F.unit(a)11 // case Request(expr, cont) => F.flatMap(expr)(cont andThen (_.runM(F)(io)))12 case Request(expr, cont) => F.flatMap(expr)(x => cont(x).runM(F)(io))13  }14 }15 case class Pure[F[_],A](get: A) extends IO[F,A]16 case class Request[F[_],I,A](expr: F[I], cont: I => IO[F,A]) extends IO[F,A]`

` 1 trait IO[F[_],A] { 2 def unit(a: A) = Pure(a) 3 def flatMap[B](f: A => IO[F,B]): IO[F,B] = this match { 4 case Pure(a) => f(a) 5 // case Request(expr,cont) => Request(expr, cont andThen (_ flatMap f)) 6 case Request(expr,cont) => Request(expr, (x: Any) => cont(x) flatMap f) 7  } 8 def map[B](f: A => B): IO[F,B] = flatMap(a => Pure(f(a))) 9 }10 case class Pure[F[_],A](get: A) extends IO[F,A]11 case class Request[F[_],I,A](expr: F[I], cont: I => IO[F,A]) extends IO[F,A]1213 trait Trampoline[A] {14 def unit(a: A): Trampoline[A] = Done(a)15 def flatMap[B](f: A => Trampoline[B]): Trampoline[B] = this match {16 case Done(a) => f(a)17 case More(k) => k() flatMap f18  }19 def map[B](f: A => B): Trampoline[B] = flatMap(a => Done(f(a)))20 }21 case class Done[A](a: A) extends Trampoline[A]22 case class More[A](k: () => Trampoline[A]) extends Trampoline[A] `

` 1 trait Free[F[_],A] { 2 private case class FlatMap[B](a: Free[F,A], f: A => Free[F,B]) extends Free[F,B] 3 def unit(a: A): Free[F,A] = Return(a) 4 def flatMap[B](f: A => Free[F,B])(implicit F: Functor[F]): Free[F,B] = this match { 5 case Return(a) => f(a) 6 case Suspend(k) => Suspend(F.map(k)(a => a flatMap f)) 7 case FlatMap(b,g) => FlatMap(b, g andThen (_ flatMap f)) 8  } 9 def map[B](f: A => B)(implicit F: Functor[F]): Free[F,B] = flatMap(a => Return(f(a)))10 }11 case class Return[F[_],A](a: A) extends Free[F,A]12 case class Suspend[F[_],A](ffa: F[Free[F,A]]) extends Free[F,A]`

` 1 trait Free[F[_],A] { 2 private case class FlatMap[B](a: Free[F,A], f: A => Free[F,B]) extends Free[F,B] 3 def unit(a: A): Free[F,A] = Return(a) 4 def flatMap[B](f: A => Free[F,B])(implicit F: Functor[F]): Free[F,B] = this match { 5 case Return(a) => f(a) 6 case Suspend(k) => Suspend(F.map(k)(a => a flatMap f)) 7 case FlatMap(b,g) => FlatMap(b, g andThen (_ flatMap f)) 8  } 910 def map[B](f: A => B)(implicit F: Functor[F]): Free[F,B] = flatMap(a => Return(f(a)))11 def resume(implicit F: Functor[F]): Either[F[Free[F,A]],A] = this match {12 case Return(a) => Right(a)13 case Suspend(k) => Left(k)14 case FlatMap(a,f) => a match {15 case Return(b) => f(b).resume16 case Suspend(k) => Left(F.map(k)(_ flatMap f))17 case FlatMap(b,g) => FlatMap(b, g andThen (_ flatMap f)).resume18  }19  }20 def liftF(fa: F[A])(implicit F: Functor[F]): Free[F,A] =21  Suspend(F.map(fa)(Return(_)))22 }23 case class Return[F[_],A](a: A) extends Free[F,A]24 case class Suspend[F[_],A](ffa: F[Free[F,A]]) extends Free[F,A] `

` 1 trait Console[A] 2 case class GetLine[A](next: A) extends Console[A] 3 case class PutLine[A](msg: String, next: A) extends Console[A] 4 implicit val consoleFunctor = new Functor[Console]{ 5 def map[A,B](ca: Console[A])(f: A => B): Console[B] = ca match { 6 case GetLine(a) => GetLine(f(a)) 7 case PutLine(m,a) => PutLine(m,f(a)) 8  } 9 } //> consoleFunctor : ch13.ex3.Functor[ch13.ex3.Console] = ch13.ex3\$\$anonfun\$ma10 //| [email protected]11 type ConsoleIO[A] = Free[Console,A]12 implicit def liftConsole[A](ca: Console[A]) = Free.liftF(ca)13 //> liftConsole: [A](ca: ch13.ex3.Console[A])ch13.ex3.Free[ch13.ex3.Console,A]14 def putLine(msg: String) = PutLine(msg,()) //> putLine: (msg: String)ch13.ex3.PutLine[Unit]15 def getLine = GetLine(()) //> getLine: => ch13.ex3.GetLine[Unit]16 val ioprg:ConsoleIO[Unit] = for {17 _ <- putLine("What is your first name ?")18 first <- getLine19 _ <- putLine("What is your last name ?")20 last <- getLine21 _ <- putLine(s"Hello, \$first \$last !")22 } yield() //> ioprg : ch13.ex3.Free[ch13.ex3.Console,Unit] = Suspend(PutLine(What is you23 //| r first name ?,Suspend(GetLine(Suspend(PutLine(What is your last name ?,Sus24 //| pend(GetLine(Suspend(PutLine(Hello, () () !,Return(())))))))))))`

` 1 trait ~>[F[_],G[_]]{ 2  def apply[A](fa: F[A]): G[A] 3 } 4 trait Free[F[_],A] { 5 private case class FlatMap[B](a: Free[F,A], f: A => Free[F,B]) extends Free[F,B] 6 def unit(a: A): Free[F,A] = Return(a) 7 def flatMap[B](f: A => Free[F,B])(implicit F: Functor[F]): Free[F,B] = this match { 8 case Return(a) => f(a) 9 case Suspend(k) => Suspend(F.map(k)(a => a flatMap f))10 case FlatMap(b,g) => FlatMap(b, g andThen (_ flatMap f))11  }1213 def map[B](f: A => B)(implicit F: Functor[F]): Free[F,B] = flatMap(a => Return(f(a)))14 def resume(implicit F: Functor[F]): Either[F[Free[F,A]],A] = this match {15 case Return(a) => Right(a)16 case Suspend(k) => Left(k)17 case FlatMap(a,f) => a match {18 case Return(b) => f(b).resume19 case Suspend(k) => Left(F.map(k)(_ flatMap f))20 case FlatMap(b,g) => FlatMap(b, g andThen (_ flatMap f)).resume21  }22  }23 def foldMap[G[_]](f: (F ~> G))(implicit F: Functor[F], G: Monad[G]): G[A] = resume match {24 case Right(a) => G.unit(a)25 case Left(k) => G.flatMap(f(k))(_ foldMap f)26  }27 }28 case class Return[F[_],A](a: A) extends Free[F,A]29 case class Suspend[F[_],A](ffa: F[Free[F,A]]) extends Free[F,A]30 object Free {31 def liftF[F[_],A](fa: F[A])(implicit F: Functor[F]): Free[F,A] =32  Suspend(F.map(fa)(Return(_)))3334 }`

` 1 type Id[A] = A 2 implicit val idMonad = new Monad[Id] { 3 def unit[A](a: A): A = a 4 def flatMap[A,B](fa: A)(f: A => B): B = f(fa) 5 } 6 object ConsoleEffect extends (Console ~> Id) { 7 def apply[A](c: Console[A]): A = c match { 8 case GetLine(n) => readLine ; n 9 case PutLine(m,n) => println(m); n10  }11 }12 ioprg.foldMap(ConsoleEffect)`

` 1 case class InOutLog(inLog: List[String], outLog: List[String]) 2 case class Logger[A](runLogger: InOutLog => (A, InOutLog)) 3 object MockConsole extends (Console ~> Logger) { 4 def apply[A](c: Console[A]): Logger[A] = Logger[A]( 5 s => (c, s) match { 6 case (GetLine(n), InOutLog(in,out)) => (in.head, InOutLog(in.tail,out)) 7 case (PutLine(l,n), InOutLog(in,out)) => ((), InOutLog(in, l :: out)) 8  } 9  )10 }1112 val s = ioprg.foldMap(MockConsole)13 s.runLogger(InOutLog(List("Tiger","Chan"),Nil))`

` 1 object NonBlockingIO extends(Console ~> Future) { 2 def apply[A](c: Console[A]): Future[A] = c match { 3 case GetLine(n) => Future.unit { 4 try Some(readLine) catch {case _: Exception => None} 5  } 6 case PutLine(n,l) => Future.unit{ 7  println(l) 8  } 9  }10 }`