【问题标题】:Free monads for recursive arithmetic expressions in Scala catsScala 猫中用于递归算术表达式的免费单子
【发布时间】:2016-07-31 07:47:07
【问题描述】:

我正在尝试使用Scala cats 库并遵循tutorial doc 定义一个简单的算术表达式求值器。

目标是以模块化的方式定义 DSL,结合整数值和可以单独定义的加法。

我有以下代码:

package examples
import cats._
import cats.data._
import cats.free._
import cats.free.Free._
import scala.language.higherKinds

object FreeExpr {

  sealed trait ValueF[A]
  case class IntValue(n: Int) extends ValueF[Int]

  class Value[F[_]](implicit I: Inject[ValueF, F]) {
    def intVal(n:Int): Free[F,Int] = inject[ValueF,F](IntValue(n))
  }

  object Value {
    implicit def value[F[_]](implicit I: Inject[ValueF,F]): Value[F] = new Value[F]
  }

  sealed trait ArithF[A]
  case class Add[A](x: A, y: A) extends ArithF[A]

  class Arith[F[_]](implicit I: Inject[ArithF, F]) {
   def add[A](x: A, y: A) : Free[F,A] =
     Free.inject[ArithF,F](Add(x,y))
  }

  object Arith {
    implicit def arith[F[_]](implicit I: Inject[ArithF,F]): Arith[F] = new Arith[F]
  }

  type Expr[A] = Coproduct[ArithF, ValueF, A]
  type Result[A] = Id[A]

  object ArithId  extends (ArithF ~> Result) {
    def apply[A](fa: ArithF[A]) = fa match {
      case Add(x,y) => ??? // for { m <- x; n <- y } yield (m + n)
    }
  }

  object ValueId extends (ValueF ~> Result) {
    def apply[A](fa: ValueF[A]) = fa match {
      case IntValue(n) => Monad[Id].pure(n)
    }
  }

  val interpreter: Expr ~> Result = ArithId or ValueId

  def expr1(implicit value : Value[Expr],
                     arith : Arith[Expr]): Free[Expr, Int] = {
    import value._, arith._
    for {
      n <- intVal(2)
      m <- add(n, n)
    } yield m
   }

  lazy val run1 = expr1.foldMap(interpreter)

}

前面的代码可以编译,因为我为 Add case 注释了“apply”的定义。我最初以为解决方案是注释的代码,但编译器返回:

[error] ...FreeExpr.scala:40: value flatMap is not a member of type parameter A
[error]       case Add(x,y) => for { m <- x; n <- y } yield (m + n)
[error]                                   ^

您知道我需要对代码进行哪些更改才能以模块化方式定义 DSL 和解释器吗?

【问题讨论】:

  • 井类型参数 A 应该是 Monad 以便能够在其上进行平面映射。 fa: ArithF[A] 不强制执行...我不确定该解决方案,因为我对 FreeMonads 不太熟悉

标签: scala scala-cats


【解决方案1】:

问题是Add 的结果类型过于笼统,在case Add(x,y) =&gt; ... 中,xy 的值类型为A 而不是M[A]

我找到了两种可能的解决方案。

一种是将Add的定义改为case class Add(x:Int,y:Int),将解释器的定义改为

object ArithId  extends (ArithF ~> Result) {
    def apply[A](fa: ArithF[A]) = fa match {
      case Add(x,y) => x + y
    }
}

我已将此解决方案添加到此要点:FreeExprInt.scala

上一个解决方案的一个问题是结果类型被固定为Int,它可能更有趣,至少是Semigroup

我尝试使用case class Add[A: Semigroup](x: A, y: A) 定义它,但编译器在处理第二个隐式参数列表时似乎存在一些问题。

一种可能的解决方法是显式添加证据。我将此解决方案添加到另一个要点:FreeExprExplicit.scala

虽然这两种解决方案都有效,但我对它们不太满意,因为我更喜欢结果类型可以是任何A 的更通用的解决方案。

有人建议我看看无标记的最终样式 (Alternatives to GADTs),但我还没有实现它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 2023-04-04
    • 2012-03-15
    • 1970-01-01
    • 2011-03-09
    相关资源
    最近更新 更多