当前位置:必发365电子游戏 > 编程 > 必发365电子游戏Free Monad是FP模式编程的主要方式
必发365电子游戏Free Monad是FP模式编程的主要方式
2019-12-19

  我们在前方花了几期时间探讨Free Monad,那是因为FP既是Monadic programming,Free Monad是FP形式编制程序的重大格局。对大家的话,Free Monad代表着fp从学术研讨到实在接受的变通,因为大家已经示范了怎么用Free Monad的算式算法关切抽离方式来落实真正的软件编制程序。可是白璧微瑕的是用Free Monad只可以编写流程式的次第;大家一定要一步一步编写翻译这种程序而望尘不及达成互相之间运算以至在编写翻译从前对前后相继结果实行分析或撤换等。这种特点能够从Monad的运算函数flatMap的函数款式里观察:def flatMap(fa: F[A])(f: A=>F[B]):F[B]。如果F[A]代表当前先后、F[B]必发365电子游戏,正是下三个主次,而下三个程序的发出依赖于F[A]运算结果,多少个runtime值。在没有做到F[A]的演算早前我们鞭比不上腹得悉F[B]的此外消息。所以又说Monadic程序布局是动态的。大家看出Free Monad效用十分刚劲:能够用Free Monad来完成其余程序,只然而那个程序构造都以动态的。动态构造程序对缓和某个品种的主题素材在效用上或者不比静态布局的次第。大家得以用Applicative来发出静态构造的程序,这些从Applicative的演算函数ap能够看得出来:def ap(f: F[A=>B])(F[A]):F[B]。大家得以这么看ap:F[A=>B]是首先段程序,F[A]是下生机勃勃段程序,而F[B]是结果程序。 第一遍之段程序都不依赖任何运算值,所以大家能够先构建那个程序然后在别的时候对那些程序进行编写翻译。由于具有程序都以牢固预见而互不影响的,所以特别适合併行运算。

与Free Monad雷同,Free Applicative Functor也是Applicative的布局化。下边是scalaz对FreeAp的概念:scalaz/FreeAp.scala

sealed abstract class FreeAp[F[_],A] {
...
  private [scalaz] case class Pure[F[_],A](a: A) extends FreeAp[F,A]
  private abstract case class Ap[F[_],A]() extends FreeAp[F,A] {
    type I
    val v: () => F[I]
    val k: () => FreeAp[F, I => A]
  }

FreeAp是意气风发种有三种景况的数据类型:case class Pure(a: A卡塔尔(英语:State of Qatar)和 case class Ap(卡塔尔{v: (卡塔尔(英语:State of Qatar)=>F[I], k: ()=> FreeAp[F, I=>A]},个中Pure既是Return,包嵌一个运算结果值A,Ap构造内的k代表了下贰个FreeAp,达成二个以Pure为终结的FreeAp布局链条。

福寿康宁了Applicative的构造化后咱们就能够流传Free Monad的算式算法关怀分离格局先编制描述成效的次第然后再对程序举办编写翻译,只然而FreeAp程序不再是在Monadic for-comprehension内的行令编制程序,而是多元的ap类函数了。与Free Monad大器晚成致,大家风流倜傥致用ADT来模拟applicative编制程序语法,然后用ap函数把ADT链接起来成为程序。大家借用scalaz.example/FreeApUsage.scala来解译:

1、定义ADT: 这里是个深入深入分析(parse卡塔尔(英语:State of Qatar)数据类型的代数语法

  // An algebra of primitive operations in parsing types from Map[String, Any]
  sealed trait ParseOp[A]
  case class ParseInt(key: String) extends ParseOp[Int]
  case class ParseString(key: String) extends ParseOp[String]
  case class ParseBool(key: String) extends ParseOp[Boolean]

2、升格:Lift to FreeAp

  // Free applicative over Parse.
  type Parse[A] = FreeAp[ParseOp, A]

  // Smart constructors for Parse[A]
  def parseInt(key: String) = FreeAp.lift(ParseInt(key))
  def parseString(key: String) = FreeAp.lift(ParseString(key))
  def parseBool(key: String) = FreeAp.lift(ParseBool(key))

FreeAp.lift 能够把其余F[A]升格成FreeAp[F,A]:

  /** Lift a value in `F` into the free applicative functor on `F` */
  def lift[F[_],A](x: => F[A]): FreeAp[F, A] = FreeAp(x, Pure((a: A) => a))

3、AST: Applicative编程

  // An example that returns a tuple of (String, Int, Boolean) parsed from Map[String, Any]
  val successfulProg: Parse[(String, Int, Boolean)] =
    (parseString("string") |@| parseInt("int") |@| parseBool("bool"))((_, _, _))

  // An example that returns a tuple of (Boolean, String, Int) parsed from Map[String, Any]
  val failedProg: Parse[(Boolean, String, Int)] =
    (parseBool("string") |@| parseString("list") |@| parseInt("bool"))((_, _, _))

能够见到地点的Applicative编程正是用|@|把FreeAp构造链接起来,然后随时把FreeAp之间的演算函数提供进去。大家知道F[A]|@|F[B]要么回到FreeAp[F,C]。也正是说那个程序的结果能够和其余FreeAp实行重新组合。大家能够看看上边包车型大巴示范:

 object algebra {
    sealed trait ConfigF[A]

    case class ConfigInt   [A](field: String, value: Int     => A) extends ConfigF[A]
    case class ConfigFlag  [A](field: String, value: Boolean => A) extends ConfigF[A]
    case class ConfigPort  [A](field: String, value: Int     => A) extends ConfigF[A]
    case class ConfigServer[A](field: String, value: String  => A) extends ConfigF[A]
    case class ConfigFile  [A](field: String, value: String  => A) extends ConfigF[A]
    case class ConfigSub   [A](field: String, value: FreeAp[ConfigF, A])   extends ConfigF[A]
  }

  object dsl {
    import algebra._

    type Dsl[A] = FreeAp[ConfigF, A]

    private def lift[A](value: ConfigF[A]): Dsl[A] = FreeAp.lift[ConfigF, A](value)

    def int   (field: String): Dsl[Int]     = lift(ConfigInt   (field, identity))
    def flag  (field: String): Dsl[Boolean] = lift(ConfigFlag  (field, identity))
    def port  (field: String): Dsl[Int]     = lift(ConfigPort  (field, identity))
    def server(field: String): Dsl[String]  = lift(ConfigServer(field, identity))
    def file  (field: String): Dsl[String]  = lift(ConfigFile  (field, identity))
    def sub[A](field: String) 
              (value: Dsl[A])               = lift(ConfigSub   (field, value))
  }

地点定义了ADT及进级函数int,flag,port...

  case class AuthConfig(port: Int, host: String)
    case class ServerConfig(logging: Boolean, auth: AuthConfig)

    val authConfig   = (int("port") |@| server("host"))(AuthConfig)
    val serverConfig = (flag("logging") |@| sub("auth")(authConfig))(ServerConfig)

如上的serverConfig就用了authConfig进行了再结合。

4、Interpret: 翻译,把描述的功效对应到具体的得以完结格局上,照旧用NaturalTransformation的办法把F[A]对应到G[A]:

  def parseOpt[A: ClassTag](a: Any): Option[A] =
    a match {
      case a: A => Some(a)
      case _ => None
    }

  // Natural transformation to Option[A]
  def toOption(input: Map[String, Any]): ParseOp ~> Option =
    new (ParseOp ~> Option) {
      def apply[A](fa: ParseOp[A]) = fa match {
        case ParseInt(key) =>
          input.get(key).flatMap(parseOpt[java.lang.Integer](_).map(x => (x: Int)))
        case ParseString(key) => input.get(key).flatMap(parseOpt[String])
        case ParseBool(key) =>
          input.get(key).flatMap(parseOpt[java.lang.Boolean](_).map(x => (x: Boolean)))
      }
    }

  // Natural transformation to ValidationNel[String, A]
  type ValidatedParse[A] = ValidationNel[String, A]
  def toValidation(input: Map[String, Any]): ParseOp ~> ValidatedParse =
    new (ParseOp ~> ValidatedParse) {
      def apply[A](fa: ParseOp[A]) = fa match {
        case s@ParseInt(_) => toOption(input)(s)
                                   .toSuccessNel(s"${s.key} not found with type Int")
        case s@ParseString(_) => toOption(input)(s)
                                   .toSuccessNel(s"${s.key} not found with type String")
        case i@ParseBool(_) => toOption(input)(i)
                                .toSuccessNel(s"${i.key} not found with type Boolean")
      }
    }

如上海展览中心示了三种程序翻译方式,对同后生可畏的主次能够用三种运算形式:

ParseOp ~> Option:翻译成Option类型。注意:input.get(key卡塔尔(英语:State of Qatar)再次回到Option,parseOpt近似再次来到Option

ParseOp ~> ValidatedPase:翻译成Validation类型。注意:不论怎样,运算进程是不会中断的,ValidationNel中会记录全体错误音信

5、运算:runner,用折叠式来对大器晚成串FreeAp布局的每八个单元举办演算,依然称作foldMap:

  /**
   * The canonical natural transformation that interprets this free
   * program by giving it the semantics of the applicative functor `G`.
   * Not tail-recursive unless `G` is a free monad.
   */
  def foldMap[G[_]:Applicative](f: F ~> G): G[A] =
    this match {
      case Pure(x) => Applicative[G].pure(x)
      case x@Ap() => Applicative[G].ap(f(x.v()))(x.k() foldMap f)
    }

运算原理超轻便:假若是Pure就回来包嵌的值;假若是Ap对下三个FreeAp k(卡塔尔(英语:State of Qatar)实行递归运算。

必发365电子游戏Free Monad是FP模式编程的主要方式。作者们用测量检验数据来运作一下:

 // An example that returns a tuple of (String, Int, Boolean) parsed from Map[String, Any]
  val successfulProg: Parse[(String, Int, Boolean)] =
    (parseString("string") |@| parseInt("int") |@| parseBool("bool"))((_, _, _))

  // An example that returns a tuple of (Boolean, String, Int) parsed from Map[String, Any]
  val failedProg: Parse[(Boolean, String, Int)] =
    (parseBool("string") |@| parseString("list") |@| parseInt("bool"))((_, _, _))

  // Test input for programs
  val testInput: Map[String, Any] =
    Map("string" -> "foobar", "bool" -> true, "int" -> 4, "list" -> List(1, 2))

  // Run that baby
  println(successfulProg.foldMap(toOption(testInput)))
  println(successfulProg.foldMap(toValidation(testInput)))
  println(failedProg.foldMap(toOption(testInput)))
  println(failedProg.foldMap(toValidation(testInput)))

上面是运算结果:

Some((foobar,4,true))
Success((foobar,4,true))
None
Failure(NonEmpty[bool not found with type Int,list not found with type String,string not found with type Boolean])

大家获取了希望的结果。

 

上一篇:没有了
下一篇:没有了