Frage

einen Hintergrund in Haskell Ich Having ist derzeit versucht, mit Scala vertraut zu machen.

Ich traf einige Probleme versuchen, eine kleine, erweiterbare Ausdruckssprache von Haskell in Scala zu übersetzen. Das zugrunde liegende Problem einen Datentyp zu schreiben, die mit den beiden neuen erweiterbar ist daten Varianten und Operationen ist allgemein bekannt als der Ausdruck Problem .

Meine ursprüngliche Lösung in Haskell verwendet Typklassen und Instanzdeklarationen mit Einschränkungen. Die Basis meines Ausdrucks wird wie folgt definiert:

module Expr where

class Expr e where
 eval :: e -> Integer

data Lit = Lit Integer
instance Expr Lit where
  eval (Lit l) = l

data Plus a b = (Expr a, Expr b) => Plus a b
instance (Expr a, Expr b) => Expr (Plus a b) where
  eval (Plus x y) = (eval x) + (eval y)

Dann habe ich eine Daten-Erweiterung, die Multiplikation fügt hinzu:

module ExprWithMul where
import Expr

data Mul a b = (Expr a, Expr b) =>  Mul a b
instance (Expr a, Expr b) => Expr (Mul a b) where
  eval (Mul x y) = (eval x) * (eval y)

Lassen Sie uns nehmen einen ziemlich-Drucker als Betriebserweiterung:

module FormatExpr where
import Expr

class (Expr t) => FormatExpr t where
  format :: t -> String

instance FormatExpr Lit where
  format (Lit l) = show l

instance (FormatExpr a, FormatExpr b) => FormatExpr (Plus a b) where
  format (Plus x y) = "(" ++ (format x) ++ "+" ++ (format y) ++ ")"

Und schließlich im vierten Modul die beiden unabhängigen Erweiterungen kombiniert werden können:

module FormatExprWithMult where
import FormatExpr
import ExprWithMul

instance (FormatExpr a, FormatExpr b) => FormatExpr (Mul a b) where
  format (Mul x y) = "(" ++ (format x) ++ "*" ++ (format y) ++ ")"

Jetzt für mein Problem: In der Regel Klassen von Haskell sind das mit implicits in Scala Konzept-Muster übersetzt. Dies ist, wie weit ich habe:

abstract class Expr[A] { // this corresponds to a type class
  def eval(e:A): Int;
}

case class Lit(v: Int)
implicit object ExprLit extends Expr[Lit] {
 def eval(e: Lit) = x.v;
}
case class Plus[A,B] (e1: A, e2: B) (implicit c1: Expr[A], c2: Expr[B])

Hier bin ich mit der Umsetzung der impliziten Objekt für Plus-stecken. Wie erkläre ich ein implizites Objekt mit Typ-Parametern und Einschränkungen?

Ich weiß, dass es andere Lösung für das Expression Problem in Scala, ich aber in dieser Version besonders interessiert.

Vielen Dank an all meine etwas längere Frage zu lesen.

War es hilfreich?

Lösung

Erster Versuch (fehlerhaft):

case class Plus[A,B] (e1: A, e2: B) (implicit c1: Expr[A], c2: Expr[B]) {
    implicit object ExprPlus extends Expr[Plus[A, B]] { 
        def eval(p:Plus[A, B]) = c1.eval(p.e1) + c2.eval(p.e2)
    }
}

Edit 1:

Das oben nicht stark genug ist (man kann nicht zwei Plus Ausdrücke hinzufügen), und die implizite Zeugnis muss nicht innerhalb der Plus Fall Klasse definiert werden ... versuchen Sie stattdessen:

case class Plus[A,B] (e1: A, e2: B) (implicit val c1: Expr[A], c2: Expr[B])
implicit def ExprPlus[A, B](implicit c1: Expr[A], c2: Expr[B]) = 
    new Expr[Plus[A, B]] { 
        def eval(p:Plus[A, B]) = c1.eval(p.e1) + c2.eval(p.e2)
    }

Edit 2:

Hier ist eine (vielleicht) etwas mehr idiomatische Version:

case class Plus[A: Expr, B: Expr] (e1: A, e2: B)
implicit def ExprPlus[A: Expr, B: Expr] = new Expr[Plus[A, B]] {
    def eval(p:Plus[A, B]) = implicitly[Expr[A]].eval(p.e1) + 
                             implicitly[Expr[B]].eval(p.e2)
}

Andere Tipps

Dies ist die komplette Umsetzung des Expression Problems in scala mit Typklassen

  trait Exp
  case class Lit(value: Int) extends Exp
  case class Add[A <: Exp, B <: Exp](left: A, right: B) extends Exp

Eval Typklasse und implizite Implementierungen

  //type class
  trait Eval[E] {
    def eval(e: E): Int
  }

  implicit def litEval = new Eval[Lit] {
    def eval(l: Lit) = l.value
  }

  implicit def addEval[A <: Exp, B <: Exp](implicit e1: Eval[A], e2: Eval[B]) = new Eval[Add[A, B]] {
    def eval(a: Add[A, B]) = e1.eval(a.left) + e2.eval(a.right)
  }

Hier können Sie die Lösung erweitern, indem neue Art Zugabe genannt Mult

case class Mult[A <: Exp, B <: Exp](left: A, right: B) extends Exp

implicit def mulEval[A <: Exp, B <: Exp](implicit e1: Eval[A], e2: Eval[B]) = new Eval[Mult[A, B]] {
    def eval(m : Mult[A, B]) = e1.eval(m.left) * e2.eval(m.right)
}

Nun können die Ausdrücke wie diese ausgewertet werden

def expressionEvaluator[A <: Exp](exp: A)(implicit e : Eval[A]) = {
    e.eval(exp)
}

def main(args: Array[String]): Unit = {
   // (3 + 4) * 7
   val exp1 = Mult(Add(Lit(3), Lit(4)), Lit(7))
   println("" + expressionEvaluator(exp1))
}

Hier können Sie das System erweitern, indem ein neuen Betrieb Druck Hinzufügen

  //type class
  trait Print[P] {
    def print(p: P): Unit
  }

  implicit def litPrint = new Print[Lit] {
    def print(l: Lit) = Console.print(l.value)
  }

  implicit def addPrint[A <: Exp, B <: Exp](implicit p1: Print[A], p2 : Print[B]) = new Print[Add[A, B]] {
    def print(a : Add[A, B]) = { p1.print(a.left); Console.print(" + "); p2.print(a.right); }
  }

  implicit def mulPrint[A <: Exp, B <: Exp](implicit p1: Print[A], p2: Print[B]) = new Print[Mult[A, B]] {
    def print(m : Mult[A, B]) = { p1.print(m.left); Console.print(" * "); p2.print(m.right) }
  }
Definieren

eine neue Methode, die Ausdrücke drucken

def printExpressions[A <: Exp](exp : A)(implicit p : Print[A]) = {
    p.print(exp)
}

Aktualisieren Sie die Hauptmethode um den Ausdruck zu drucken

def main(args: Array[String]): Unit = {
    val exp1 = Mult(Add(Lit(3), Lit(4)), Lit(7))

    print("Expression : ")
    printExpressions(exp1)
    print(", Evaluated to : " + expressionEvaluator(exp1))
}

Entire Lösung kann durch Umwickeln Sie den Code innerhalb eines Objekts ausgeführt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top