2015-08-17 25 views
6

我指的是下面列出的Ken Scambler的源代码,也请参阅GitHub source免费单子和AST的关系

package kenbot.free 

import scalaz._ 
import Scalaz._ 
import Free._ 
import scala.collection.mutable 

// This example is based off the one in Runar Bjarnason's "Dead Simple Dependency Injection" talk. 
// http://www.youtube.com/watch?v=ZasXwtTRkio 


// 0. Fantasy API 
// def put(key: String, value: String): Unit 
// def get(key: String): String 
// def delete(key: String): Unit 


// 1. ADT 
sealed trait KVS[+Next] 
case class Put[Next](key: String, value: String, next: Next) extends KVS[Next]  // <---- def put(key: String, value: String): Unit 
case class Get[Next](key: String, onResult: String => Next) extends KVS[Next]  // <---- def get(key: String): String 
case class Delete[Next](key: String, next: Next) extends KVS[Next]     // <---- def delete(key: String): Unit 


object KVS { 
    type Script[A] = Free[KVS, A] 

    // 2. Functor definition 
    implicit val functor: Functor[KVS] = new Functor[KVS] { 
    def map[A,B](kvs: KVS[A])(f: A => B): KVS[B] = kvs match { 
     case Put(key, value, next) => Put(key, value, f(next)) 
     case Get(key, onResult) => Get(key, onResult andThen f) 
     case Delete(key, next) => Delete(key, f(next)) 
    } 
    } 

    // 3. Lifting functions 
    def put(key: String, value: String): Script[Unit] = liftF(Put(key, value,())) 

    def get(key: String): Script[String] = liftF(Get(key, identity)) 

    def delete(key: String): Script[Unit] = liftF(Delete(key,())) 


    // 4. Composite functions 
    def modify(key: String, f: String => String): Free[KVS, Unit] = for { 
    v <- get(key) 
    _ <- put(key, f(v)) 
    } yield() 


    // 5. Write scripts 
    val script: Free[KVS, Unit] = for { 
    id <- get("swiss-bank-account-id") 
    _ <- modify(id, (_ + 1000000)) 
    _ <- put("bermuda-airport", "getaway car") 
    _ <- delete("tax-records") 
    } yield() 


    // 6. Interpreters 

    // Building an immutable structure 
    def interpretPure(kvs: Script[Unit], table: Map[String, String] = Map.empty): Map[String, String] = kvs.resume.fold({ 
    case Get(key, onResult) => interpretPure(onResult(table(key)), table) 
    case Put(key, value, next) => interpretPure(next, table + (key -> value)) 
    case Delete(key, next) => interpretPure(next, table - key) 
    }, _ => table) 


    // Directly running effects 
    def interpretImpure(kvs: Script[Unit], table: mutable.Map[String, String]): Unit = kvs.go { 
    case Get(key, onResult) => onResult(table(key)) 
    case Put(key, value, next) => table += (key -> value); next 
    case Delete(key, next) => table -= key; next 
    } 
} 

如果按照下列slides,任何脚本(见源代码5)被“转化”成免费的单子中类似Suspend(Op(Suspend(Op(......(Result(Op))..))

不幸的是,5下的脚本只是一个线性的命令序列。

即使搜索了几个小时的网在,我无法找到任何的例子,给了以下的问题更深入的了解:

  1. 线性操作的顺序(这也是树当然)由Suspend(Op(Suspend(Op(......(Result(Op))..))表示,并且因此是AST的表示。这个假设是正确的吗?
  2. AST如何表示真实表示免费单子内的AST?我认为,发生这种情况时,包括控制结构? (例如根据条件左右树枝)。有人可以举例说明一个真正的AST起作用的例子吗?也许,举例说明如何在给定的例子中实现“if”。 (?在源代码下5给出)
  3. 什么是一般的方法,包括控制结构成脚本

PS:请尝试坚持斯卡拉/ ScalaZ,因为Haskell是(还)没有我的领域的专业知识。

回答

5

在Scalaz中,Free单子为两种情况(简化并忽略GoSub优化):

sealed abstract class Free[S[_], A] 
    case class Return[S[_], A](a: A) extends Free[S, A] 
    case class Suspend[S[_], A](a: S[Free[S, A]]) extends Free[S, A] 

让我们先来看看有什么Free.liftF呢,例如对于

def get(key: String): Script[String] = liftF(Get(key, identity)) 

get("key")时,我们会得到

get("key") 
// definition of get 
liftF(Get("key",identity) 
// definition of liftF 
Suspend(Get("key",identity).map(Return) 
// definition of map for Get above 
Suspend(Get("key", identity andThen Return)) 
// identity andThen f == f 
Suspend(Get("key", Return)) 

有了这样的,让我们开始您的问题:

  1. 线性操作的顺序(这也是树当然)由Suspend(Op(Suspend(Op(......(Result(Op))..))表示,因此是AST的表示?这个假设是否正确?

本质是,写在使用从函子所产生的自由单子所述DSL的程序表示的“台阶”链,其中每个步骤是任一含有的你的算符例的一个或一个Return一个Suspend表示端的链条。

作为一个具体的例子,script看起来大约是这样的:

Suspend(Get("swiss-bank-account-id", 
    id => Suspend(Get(id, 
    v => Suspend(Put(id, v+1000000, 
     _ => Suspend(Put("bermuda-airport","getaway car", 
     _ => Suspend(Delete("tax-records", 
      _ => Return(()) 
     )))))))))) 

正如你所看到的,我们基本上只是“补”我们与计算的延续函子的孔,用Return终止。在示例DSL中,我们将始终有一个线性链,因为每个KVS仿函数都只有一个“空洞”来填充,所以没有分支。

  1. 真正的AST如何代表免费monad?我认为,发生这种情况时,包括控制结构? (例如根据条件左右树枝)。有人可以举例说明一个真正的AST起作用的例子吗?也许,举例说明如何在给定的例子中实现“if”。

让我们扩展我们的DSL与分支结构:

case class Cond[Next](cond: Boolean, ifTrue: Free[KVS,Next], ifFalse: Free[KVS,Next]) extends KVS[Next] 
def cond[A](c: Boolean)(ifTrue: => Script[A])(ifFalse: => Script[A]): Script[A] = 
    liftF(Cond(c,ifTrue,ifFalse)) 

改变解释的情况后,就可以用这样的:那么现在

val script2: Script[Unit] = for { 
    id <- get("swiss-bank-account-id") 
    _ <- cond(id == "123") { 
    Free.point[KVS,Unit](()) 
    } { 
    for { 
     _ <- modify(id, ("LOTS OF " + _)) 
     _ <- put("bermuda-airport", "getaway car") 
     _ <- delete("tax-records") 
    } yield() 
    } 
} yield() 

你有一个“真正的“AST,我把你的”真实“解释为”具有分支节点“,而不是直到现在为止的线性链形式。如预期的输出:

println(interpretPure(
    script2, 
    Map("swiss-bank-account-id" -> "42", "42" -> "money", "tax-records" -> "acme corp"))) 
// Map(swiss-bank-account-id -> 42, 42 -> LOTS OF money, bermuda-airport -> getaway car) 

println(interpretPure(
    script2, 
    Map("swiss-bank-account-id" -> "123", "tax-records" -> "acme corp"))) 
// Map(swiss-bank-account-id -> 123, tax-records -> acme corp) 
  • 什么是一般的方法,包括控制结构成脚本
  • (如在源代码下5中给出?)

    首先,请记住,你可以例如使用标准内,如果用于-内涵:

    val script3: Script[Unit] = for { 
        id <- get("swiss-bank-account-id") 
        _ <- if (id == "123") { 
        Free.point[KVS,Unit](()) 
        } else { 
        for { 
         _ <- modify(id, ("LOTS OF " + _)) 
         _ <- put("bermuda-airport", "getaway car") 
         _ <- delete("tax-records") 
        } yield() 
        } 
    } yield() 
    

    塞康请记住,由于Script[A]只是Free[KVS,A]这个事实,您有一个monad可供选择,因此任何“控制结构”在例如Scalaz的单子会为你工作了:

    val script4: Script[Unit] = modify("key",_+"!").untilM_ { get("key").map(_.length > 42) } 
    
    println(interpretPure(script4, Map("key" -> ""))) 
    // Map(key -> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!) 
    

    Monad.scalaMonadSyntax.scala看一看,这里还有whileMiterateWhile