Digitare classi in Scala
-
01-10-2019 - |
Domanda
Avere un background in Haskell Attualmente sto cercando di acquisire familiarità con Scala.
Ho incontrato alcuni problemi cercando di tradurre una piccola, linguaggio delle espressioni estensibile da Haskell in Scala. Il problema alla base della scrittura un tipo di dati che è estensibile sia con nuovi dati-varianti e operazioni è comunemente noto come href="http://en.wikipedia.org/wiki/Expression_Problem" rel="noreferrer"> espressione .
La mia soluzione originale in Haskell utilizza le classi di tipo e le dichiarazioni di istanza con vincoli. La base della mia espressione è definito come segue:
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)
Poi, ho una tecnica d'estensione che aggiunge la moltiplicazione:
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)
Diamo uno pretty-stampante come estensione 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) ++ ")"
E, infine, nel quarto modulo le due estensioni indipendenti possono essere combinati:
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) ++ ")"
Ora per il mio problema: Di solito digitare classi da Haskell sono convertite al concetto-modello con impliciti a Scala. Questo è quanto ho ottenuto:
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])
Qui mi sono bloccato con l'attuazione del oggetto implicito per più. Come faccio a dichiarare un oggetto implicita con parametri di tipo e vincoli?
So che ci sono altre soluzioni per il problema espressione in Scala, sono però interessati a questa versione in particolare.
Grazie a tutti per aver letto la mia domanda un po 'lungo.
Soluzione
Primo tentativo (imperfetto):
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)
}
}
Modifica 1:
È possibile che questo non è sufficientemente potente (non è possibile aggiungere due espressioni Plus
), e la necessità di testimone implicito non essere definita all'interno della classe caso Plus
... provate questo invece:
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)
}
Modifica 2:
Ecco una versione (forse) un po 'più idiomatica:
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)
}
Altri suggerimenti
Questa è la completa implementazione di espressione problema in Scala utilizzando le classi di tipo
trait Exp
case class Lit(value: Int) extends Exp
case class Add[A <: Exp, B <: Exp](left: A, right: B) extends Exp
tipo Eval classe e le implementazioni implicite
//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)
}
Consente di estendere la soluzione con l'aggiunta di nuovo tipo chiamato 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)
}
Ora le espressioni possono essere valutate come questo
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))
}
Consente di estendere il sistema con l'aggiunta di una nuova operazione di stampa
//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) }
}
definire un nuovo metodo per stampare le espressioni
def printExpressions[A <: Exp](exp : A)(implicit p : Print[A]) = {
p.print(exp)
}
Aggiornare il principale metodo per stampare l'espressione
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))
}
L'intera soluzione può essere eseguita avvolgendo il codice all'interno di un oggetto.