Scala猫中递归算术表达式的自由单子

Scala猫中递归算术表达式的自由单子,scala,scala-cats,Scala,Scala Cats,我正在尝试使用库定义一个简单的算术表达式计算器,并遵循以下步骤 目标是以模块化的方式定义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] ca

我正在尝试使用库定义一个简单的算术表达式计算器,并遵循以下步骤

目标是以模块化的方式定义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)

}
包示例
进口猫_
导入cats.data_
进口猫。免费_
进口猫。免费。免费_
导入scala.language.higherKinds
对象自由表达式{
密封特性值F[A]
case类IntValue(n:Int)扩展了ValueF[Int]
类值[F[]](隐式I:Inject[ValueF,F]){
def intVal(n:Int):自由[F,Int]=注入[ValueF,F](IntValue(n))
}
对象值{
隐式定义值[F[]](隐式I:Inject[ValueF,F]):值[F]=新值[F]
}
密封式HF[A]
案例类添加[A](x:A,y:A)扩展了ArithF[A]
类Arith[F[]](隐式I:Inject[ArithF,F]){
定义添加[A](x:A,y:A):自由[F,A]=
自由注入[ArithF,F](添加(x,y))
}
对象算术{
隐式定义arith[F[]](隐式I:Inject[ArithF,F]):arith[F]=新的arith[F]
}
类型Expr[A]=副产品[ArithF,ValueF,A]
类型结果[A]=Id[A]
对象ArithId扩展(ArithF~>Result){
def应用[A](fa:ArithF[A])=fa匹配{
格加法(x,y)=>?//对于{m Monad[Id]。纯(n)
}
}
val解释器:Expr~>Result=ArithId或ValueId
def expr1(隐式值:值[Expr],
arith:arith[Expr]:Free[Expr,Int]={
导入值。\u,arith_
为了{

n问题在于
Add
的结果类型过于笼统,在
案例中,Add(x,y)=>…
x
y
的类型为
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
    }
}
我已将此解决方案添加到此要点中:

先前解决方案的一个问题是,结果类型固定为
Int
,可能更有趣的是,它可以是至少一个
半群

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

一个可能的解决方法是明确添加证据。我将此解决方案添加到另一个要点中:

虽然这两种解决方案都有效,但我对它们并不十分满意,因为我更喜欢一种更通用的解决方案,其中结果类型可以是任意
a


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

井型参数a应该是单子,以便能够在其上进行平面映射。
fa:ArithF[a]
没有强制执行它……我不确定解决方案,因为我对FreeMonads不太熟悉
object ArithId  extends (ArithF ~> Result) {
    def apply[A](fa: ArithF[A]) = fa match {
      case Add(x,y) => x + y
    }
}