Escriba clases en Scala
-
01-10-2019 - |
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.
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.