Question

Avoir une formation en Haskell Je tente actuellement de se familiariser avec Scala.

Je rencontrais des problèmes en essayant de traduire un petit langage d'expression extensible de Haskell en Scala. La question sous-jacente de l'écriture d'un type de données qui est extensible avec les nouvelles données variantes et des opérations est communément appelé l'expression problème .

Ma solution originale en Haskell utilise des classes de type et les déclarations d'exemple avec des contraintes. La base de mon expression est définie comme suit:

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)

Alors, j'ai une des données d'extension qui ajoute la multiplication:

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)

Jetons un joli imprimante comme une extension opérationnelle:

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) ++ ")"

Et enfin, dans le quatrième module, les deux extensions indépendantes peuvent être combinées:

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) ++ ")"

Maintenant, pour mon problème: est habituellement en cours de haskell sont convertis au concept-modèle avec implicits à Scala. C'est de savoir jusqu'où je suis arrivé:

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])

Ici, je suis coincé avec la mise en œuvre de l'objet implicite Plus. Comment puis-je déclarer un objet implicite avec des paramètres et des contraintes de type?

Je sais qu'il ya d'autres solutions pour le problème d'expression dans Scala, je suis cependant intéressé par cette version en particulier.

Merci à vous tous d'avoir lu ma question assez longue.

Était-ce utile?

La solution

Première tentative (imparfaite):

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:

Ce qui précède n'est pas assez puissant (vous ne pouvez pas ajouter deux expressions Plus), et la nécessité de témoin implicite de ne pas être défini à l'intérieur de la classe affaire Plus ... essayez ceci:

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:

Voici une (peut-être) version légèrement plus idiomatiques:

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)
}

Autres conseils

Voici la mise en œuvre complète d'expression Problème dans scala en utilisant des classes de type

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

classe de type Eval et implémentations implicites

  //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)
  }

permet d'étendre la solution en ajoutant un nouveau type appelé 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)
}

Maintenant, les expressions peuvent être évaluées comme ceci

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))
}

permet d'étendre le système en ajoutant une nouvelle impression de l'opération

  //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) }
  }

Définir une nouvelle méthode pour imprimer les expressions

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

Mettre à jour la principale méthode pour imprimer l'expression

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))
}

solution entière peut être exécutée en enroulant le code à l'intérieur d'un objet.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top