gpt4 book ai didi

scala - Scala cats 中递归算术表达式的免费单子(monad)

转载 作者:行者123 更新时间:2023-12-04 21:58:17 27 4
gpt4 key购买 nike

我正在尝试使用 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 案例的“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 和解释器吗?

最佳答案

问题在于 Add 的结果类型过于笼统,在 case 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
}
}

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

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

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

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

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

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

关于scala - Scala cats 中递归算术表达式的免费单子(monad),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38681880/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com