Scalaz(42)- Free :FreeAp-Applicative Style Programming LanguageScalaz(42)- Free :FreeAp-Applicative Style Programming Language

 
我们于前面花了几乎梦想时讨论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]不畏是生一个次,而下一个序的发生依赖性让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,那是盖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]就算是下一个程序,而生一个顺序的起依赖让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

与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]
  }
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)和 case class Ap(){v: ()=>F[I], k: ()=>
FreeAp[F,
I=>A]},其中Pure既是Return,包嵌一个运算结果值A,Ap结构外的k代表了产一个FreeAp,实现一个坐Pure为终结的FreeAp结构链条。

FreeAp是同等栽出个别种植状态的数据类型:case
class Pure(a: A)和 case class Ap(){v: ()=>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来解译:

兑现了Applicative的结构化后我们就是得流传Free
Monad的算式算法关注分离模式先修描述功能的主次然后还指向程序开展编译,只不过FreeAp程序不再是当Monadic
for-comprehension内的行令编程,而是多元的ap类函数了。与Free
Monad一致,我们同用ADT来模拟applicative编程语法,然后用ap函数把ADT链接起来成为程序。我们借scalaz.example/FreeApUsage.scala来解译:

1、定义ADT:
这里是单解析(parse)数据类型的代数语法

1、定义ADT:
这里是独解析(parse)数据类型的代数语法

  // 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]
  // 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

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))
  // 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]:

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))
  /** 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编程

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"))((_, _, _))
  // 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进行组合。我们可以省下面的言传身教:

可看看上面的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))
  }
 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…

方定义了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)
  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进行了双重结合。

以上之serverConfig就就此了authConfig进行了再做。

4、Interpret:
翻译,把叙的效应对承诺到具体的贯彻方式上,还是用NaturalTransformation的不二法门把F[A]对应到G[A]:

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")
      }
    }
  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)返回Option,parseOpt同样返回Option

ParseOp ~>
Option:翻译成Option类型。注意:input.get(key)返回Option,parseOpt同样返回Option

ParseOp ~>
ValidatedPase:翻译成Validation类型。注意:无论如何,运算过程是免会见半途而废的,ValidationNel中见面记录有错误信息

ParseOp ~>
ValidatedPase:翻译成Validation类型。注意:无论如何,运算过程是勿见面搁浅的,ValidationNel中会记录有错误信息

5、运算:runner,用折叠式来针对同错FreeAp结构的诸一个单元进行演算,还是称作foldMap:

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)
    }
  /**
   * 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()进行递归运算。

运算原理非常简短:如果是Pure就回到包嵌的值;如果是Ap对生一个FreeAp
k()进行递归运算。

我们因此测试数据来运作一下:

咱们用测试数据来运行一下:

 // 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)))
 // 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])
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])

我们沾了想的结果。

咱们得到了希望的结果。

 

 

相关文章