加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 在翻译中使用单子的要点是什么?

发布时间:2020-12-16 19:04:15 所属栏目:安全 来源:网络整理
导读:我最近发现这个小小的 scala example叫做简单的解释器,使用monads: object simpleInterpreter { case class M[A](value: A) { def bind[B](k: A = M[B]): M[B] = k(value) def map[B](f: A = B): M[B] = bind(x = unitM(f(x))) def flatMap[B](f: A = M[B])
我最近发现这个小小的 scala example叫做简单的解释器,使用monads:

object simpleInterpreter {

  case class M[A](value: A) {
    def bind[B](k: A => M[B]): M[B] =  k(value)
    def map[B](f: A => B): M[B] =  bind(x => unitM(f(x)))
    def flatMap[B](f: A => M[B]): M[B] = bind(f)
  }

  def unitM[A](a: A): M[A] = M(a)

  def showM(m: M[Value]): String = m.value.toString();

  type Name = String

  trait Term;
  case class Var(x: Name) extends Term
  case class Con(n: int) extends Term
  case class Add(l: Term,r: Term) extends Term
  case class Lam(x: Name,body: Term) extends Term
  case class App(fun: Term,arg: Term) extends Term

  trait Value
  case object Wrong extends Value {
   override def toString() = "wrong"
  } 
  case class Num(n: int) extends Value {
    override def toString() = n.toString()
  }
  case class Fun(f: Value => M[Value]) extends Value {
    override def toString() = "<function>"
  }

  type Environment = List[Pair[Name,Value]]

  def lookup(x: Name,e: Environment): M[Value] = e match {
    case List() => unitM(Wrong)
    case Pair(y,b) :: e1 => if (x == y) unitM(b) else lookup(x,e1)
  }

  def add(a: Value,b: Value): M[Value] = Pair(a,b) match {
    case Pair(Num(m),Num(n)) => unitM(Num(m + n))
    case _ => unitM(Wrong)
  }

  def apply(a: Value,b: Value): M[Value] = a match {
    case Fun(k) => k(b)
    case _ => unitM(Wrong)
  }

  def interp(t: Term,e: Environment): M[Value] = t match {
    case Var(x) => lookup(x,e)
    case Con(n) => unitM(Num(n))
    case Add(l,r) => for (val a <- interp(l,e);
               val b <- interp(r,e);
               val c <- add(a,b))
                      yield c
    case Lam(x,t) => unitM(Fun(a => interp(t,Pair(x,a) :: e)))
    case App(f,t) => for (val a <- interp(f,e);
               val b <- interp(t,e);
               val c <- apply(a,b))
              yield c
  }

  def test(t: Term): String = 
    showM(interp(t,List()))

  val term0 = App(Lam("x",Add(Var("x"),Var("x"))),Add(Con(10),Con(11)))
  val term1 = App(Con(1),Con(2))

  def main(args: Array[String]) {
    println(test(term0))
    println(test(term1))
  }
}

这里的单体计算有什么用途/优点?事实上,M只不过是一个身份的monad.这仅仅是为了给出一个单一语法的例子,还是有一个重要的作用?

解决方法

以下是Phil Wadler的论文的一个小结:
当您直接编写“直接”风格的解释器时,在添加新功能时,很多代码必须更改.例如,如果添加异常,则必须检查异常是否引发任何可能评估表达式的位置,即使该构造类似于if或while或函数调用,因此与异常无关.

如果您用单体式编写解释器,则可以通过更改monad来添加新功能.您通常还会添加一些新的语法来支持该功能,但是其余的代码都不会更改.因此,单体风格是一种在语言变化方面成为模块化的口译员的方式.

例子:

>要添加异常,请将monad更改为错误monad,为throw和catch添加新的语法和代码,并且您的其他代码都不会更改.
>要改变语言,使得表达式的值是一个概率分布,而不仅仅是一个值,改变monad,并添加一个概念性的结构,如“翻转偏向的硬币”.再次,没有一个旧的代码改变. (这个真的很有趣,我有done it myself.)

现在我已经告诉过你,单体计算的优点是什么,我最好告诉你最大的缺点:一次只能做一个有趣的功能.原因是,一般来说,你不能组成两个monad来制作一个新的monad.这不仅仅是一般的,而是你可能真正喜欢使用的单子.

如果您真的有兴趣制作模块化解释器,您可以在其中轻松实验不同的语言功能组合(而不仅仅是单个功能),您需要使用单变量变换器.盛良,保罗·哈达克和马克·琼斯在Monad Transformers and Modular Interpreters上写了一篇很棒的论文.这是一个很好的阅读我推荐它很高.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读