2015-08-17 22 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