2016-07-31 3 views
2

Scala catsライブラリを使用し、tutorial docの後ろにある簡単な算術式評価ツールを定義しようとしています。Scala猫の再帰的算術式のためのフリーモナド

目的は、別々に定義できる整数値と加算を組み合わせたモジュール式で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) 

} 

。私は当初、ソリューションがコメントされたコードが、コンパイラが戻ると思った:

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

+0

フラットにできるようにするには、よくタイプするパラメータAはMonadでなければなりません。 'fa:ArithF [A]'はそれを強制しません...私はFreeMonadsにあまり精通していないので、私は決議についてはわかりません –

答えて

1

問題はAddの結果タイプが一般的すぎたとcase Add(x,y) => ...の値xyA代わりのM[A]を入力していたということでした。

可能な解決策が2つ見つかりました。以前のソリューションのFreeExprInt.scala

一つの問題:

一つは、私はこの趣旨にこのソリューションを追加した

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

するcase class Add(x:Int,y:Int)と通訳の定義をするAddの定義を変更することです結果の型がIntに固定されていることがあり、少なくともSemigroupの方が面白い可能性があります。

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

可能な回避策は、証拠を明示的に追加することです。私はこの解決策を別の要点に追加しました:FreeExprExplicit.scala

両方の解決法が機能しますが、結果の種類がAの可能性があるより一般的な解決策を望むので、私はそれほど満足できません。

タグレスの最終的なスタイル(Alternatives to GADTs)を見てみることをお勧めしましたが、まだ実装していません。

関連する問題