Pregunta

Tener un fondo en Haskell Actualmente estoy tratando de familiarizarse con Scala.

Me encontré con algunos problemas al tratar de traducir un pequeño, el lenguaje extensible de expresión Haskell en Scala. El problema subyacente de la escritura de un tipo de datos que es extensible con las dos nuevas variantes de datos y las operaciones se conoce comúnmente como la href="http://en.wikipedia.org/wiki/Expression_Problem" rel="noreferrer"> expresión problema.

Mi solución original en Haskell utiliza clases de tipos y declaraciones de instancias con limitaciones. La base de mi expresión se define como sigue:

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)

A continuación, tengo una técnica de extensión que añade la multiplicación:

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)

Vamos a dar un bonito-impresora como una extensión operativa:

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

Y, por último, en el cuarto módulo de las dos extensiones independientes se pueden combinar:

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

Ahora mi problema: Por lo general, el tipo clases de Haskell son traducidos al concepto de patrón con implícitos en Scala. Esto es lo lejos que tengo:

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

Aquí estoy atascado con la implementación del objeto implícito para Plus. ¿Cómo puedo declarar un objeto implícito con parámetros de tipo y limitaciones?

Sé que hay otra solución para el problema de la expresión en Scala, sin embargo estoy interesado en esta versión en particular.

Gracias a todos por leer mi pregunta algo largo.

¿Fue útil?

Solución

Primer intento (defectuoso):

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

Editar 1:

Lo anterior no es lo suficientemente potente (no se puede agregar dos expresiones Plus), y la necesidad implícita testigo no se define dentro de la clase caso Plus ... prueba este lugar:

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

Editar 2:

Aquí hay una versión (quizás) un poco más idiomática:

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

Otros consejos

Esta es la aplicación completa de Expresión problema en Scala uso de clases Tipo

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

clase de tipo Eval y las implementaciones implícitos

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

Le permite ampliar la solución mediante la adición de nuevo tipo llamado 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)
}

Ahora las expresiones pueden ser evaluadas como esto

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

Le permite ampliar el sistema mediante la adición de una nueva operación de impresión

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

Definir un nuevo método para imprimir las expresiones

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

Actualizar el principal método para imprimir la expresión

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

solución completa puede ser ejecutado por envolver el código dentro de un objeto.

scroll top