scala – 在翻译中使用单子的要点是什么?
我最近发现这个小小的
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来制作一个新的monad.这不仅仅是一般的,而是你可能真正喜欢使用的单子. 如果您真的有兴趣制作模块化解释器,您可以在其中轻松实验不同的语言功能组合(而不仅仅是单个功能),您需要使用单变量变换器.盛良,保罗·哈达克和马克·琼斯在Monad Transformers and Modular Interpreters上写了一篇很棒的论文.这是一个很好的阅读我推荐它很高. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |