简体   繁体   English

了解scalaz中的免费monad

[英]Understanding Free monad in scalaz

I'm experimenting with Free monad in Scalaz and trying to build simple interpreter to parse and evaluate expressions like: 我正在Scalaz试用Free monad,并尝试构建简单的解释器来分析和评估表达式,例如:

dec(inc(dec(dec(10)))

where dec means decrement, inc means increment. 其中dec表示递减, inc表示递增。 Here is what I got: 这是我得到的:

trait Interpreter[A]
case class V[A](a: A) extends Interpreter[A]

object Inc {
  private[this] final val pattern = Pattern.compile("^inc\\((.*)\\)$")
  def unapply(arg: String): Option[String] = {
    val m = pattern.matcher(arg)
    if(m.find()){
      Some(m.group(1))
    } else None
  }
}

object Dec {
  private[this] final val pattern = Pattern.compile("^dec\\((.*)\\)$")
  def unapply(arg: String): Option[String] = {
    val m = pattern.matcher(arg)
    if(m.find()){
      Some(m.group(1))
    } else None
  }
}

object Val {
  def unapply(arg: String): Option[Int] =
    if(arg.matches("^[0-9]+$")) Some(Integer.valueOf(arg))
    else None
}

Now this is all I need to build AST. 现在,这就是构建AST所需的全部内容。 It currently looks as follows: 当前看起来如下:

def buildAst(expression: String): Free[Interpreter, Int] = 
   expression match {
     case Inc(arg) => inc(buildAst(arg))
     case Dec(arg) => dec(buildAst(arg))
     case Val(arg) => value(arg)
}

private def inc(i: Free[Interpreter, Int]) = i.map(_ + 1)
private def dec(d: Free[Interpreter, Int]) = d.map(_ - 1)
private def value(v: Int): Free[Interpreter, Int] = Free.liftF(V(v))

Now when testing the application: 现在在测试应用程序时:

object Test extends App{

  val expression = "inc(dec(inc(inc(inc(dec(10))))))"

  val naturalTransform = new (Interpreter ~> Id) {
    override def apply[A](fa: Interpreter[A]): Id[A] = fa match {
      case V(a) => a
    }
  }

  println(buildAst(expression).foldMap(naturalTransform)) //prints 12
}

And it works pretty much fine (I'm not sure about if it is in scalaz style). 而且效果很好(我不确定它是否为scalaz风格)。

THE PROBLEM is the extractor objects Inc , Dec , Val feels like boilerplate code. 问题是提取器对象IncDecVal感觉像样板代码。 Is there a way to reduce such a code duplication. 有没有办法减少这种代码重复。

This will definitely become a problem if the number of functions supported gets larger. 如果支持的功能数量增加,那么这肯定会成为问题。

Free monads are creating some boilerplate and that is a fact. 自由单子正在创造一些样板,这是事实。 However if you are willing to stick to some conventions, you could rewrite interpreter with Freasy Monad : 但是,如果您愿意遵守某些约定,则可以使用Freasy Monad重写解释器:

@free trait Interpreter {
  type InterpreterF[A] = Free[InterpreterADT, A]
  sealed trait InterpreterADT[A]


  def inc(arg: InterpreterF[Int]): InterpreterF[Int]
  def dec(arg: InterpreterF[Int]): InterpreterF[Int]
  def value(arg: Int): InterpreterF[Int]
}

and that would generate all of case classes and matching on them. 这将生成所有案例类并对其进行匹配。 The interpreter becomes just a trait to implement. 解释器只是实现的一个特征。

However, you already have some logic within unapply - so you would have to split the parsing and executing logic: 但是,您已经在不应用中包含了一些逻辑-因此您必须拆分解析和执行逻辑:

import Interpreter.ops._
val incP = """^inc\\((.*)\\)$""".r
val decP = """^dec\\((.*)\\)$""".r
val valP = """^val\\((.*)\\)$""".r
def buildAst(expression: String): InterpreterF[Int] = expression match {
  case incP(arg) => inc(buildAst(arg))
  case decP(arg) => dec(buildAst(arg))
  case valP(arg) => value(arg.toInt)
}

Then you could implement an actual interpreter: 然后,您可以实现一个实际的解释器:

val impureInterpreter = new Interpreter.Interp[Id] {
  def inc(arg: Int): Int = arg+1
  def dec(arg: Int): Int = arg-1
  def value(arg: Int): Int = arg
}

and run it: 并运行它:

impureInterpreter.run(buildAst(expression))

I admit that this is more of a pseudocode than tested working solution, but it should give a general idea. 我承认,这比经过测试的工作解决方案更像是伪代码,但是应该给出一个总体思路。 Another library that uses similar idea is Freestyle but they use their own free monads implementation instead of relying on a cats/scalaz. 另一个使用类似想法的库是Freestyle,但是它们使用自己的免费monads实现,而不是依赖cats / scalaz。

So, I would say it is possible to remove some boilerplate as long as you have no issue with splitting parsing and interpretation. 因此,我想只要您对拆分解析和解释没有问题,就可以删除一些样板。 Of course not all can be removed - you have to declare possible operations on your Interpreter algebra as well as you have to implement interpreter yourself. 当然,并非所有内容都可以删除-您必须在Interpreter代数上声明可能的操作,以及必须自己实现解释器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM