3

Scala の catライブラリを使用し、チュートリアル 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 ケースの「適用」の定義をコメントしたため、前のコードはコンパイルされます。最初は、解決策はコメントされているコードだと思っていましたが、コンパイラは次のように返します。

[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 とインタープリターを定義するには、コードの何を変更する必要があるか知っていますか?

4

1 に答える 1

1

問題は、 の結果の型がAdd一般的すぎcase Add(x,y) => ...て値xに含まれていて、 の代わりにy型があったことでした。AM[A]

私は2つの可能な解決策を見つけました。

Add1 つは、becase 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

前のソリューションの問題の 1 つは、結果の型が に固定されてIntいることであり、少なくともSemigroup.

を使用して定義しようとしましcase class Add[A: Semigroup](x: A, y: A)たが、コンパイラには、2 番目の暗黙のパラメーター リストを処理する際に問題があるようです。

考えられる回避策は、証拠を明示的に追加することです。このソリューションを別の要点に追加しました: FreeExprExplicit.scala

どちらのソリューションも機能しますが、結果の型が any になる可能性のあるより一般的なソリューションを好むため、私はそれらに完全には満足していませんA

タグなしの最終スタイル ( Alternatives to GADTs ) を検討するように提案されましたが、まだ実装していません。

于 2016-08-01T07:45:05.703 に答える