اكتب الفصول في سكالا
-
01-10-2019 - |
سؤال
امتلاك خلفية في Haskell أحاول حاليًا التعرف على Scala.
واجهت بعض المشكلات في محاولة لترجمة لغة تعبير صغيرة قابلة للتمديد من هاسكل إلى سكالا. يُعرف القضية الأساسية المتمثلة في كتابة نوع بيانات يمكن توسيعها مع كل من أدوات البيانات والعمليات الجديدة المعروفة باسم مشكلة التعبير.
يستخدم الحل الأصلي في Haskell فئات النوع وإعلانات المثيلات مع القيود. يتم تعريف قاعدة تعبيري على النحو التالي:
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)
ثم ، لدي تمديد بيانات واحد يضيف الضرب:
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)
لنأخذ طباعة جميلة كملحق تشغيلي:
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) ++ ")"
وأخيراً ، في الوحدة الرابعة ، يمكن الجمع بين الامتدادات المستقلة:
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) ++ ")"
الآن لمشكلتي: عادة ما يتم ترجمة الفصول الدراسية من Haskell إلى النمط المفهوم مع ضمني في Scala. هذا هو المدى الذي وصلت إليه:
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])
أنا هنا عالق في تنفيذ الكائن الضمني لـ Plus. كيف أعلن كائنًا ضمنيًا مع معلمات وقيود النوع؟
أعلم أن هناك حلًا آخر لمشكلة التعبير في Scala ، ومع ذلك ، فأنا مهتم بهذا الإصدار على وجه الخصوص.
شكرا لكم جميعا على قراءة سؤالي المطول إلى حد ما.
المحلول
المحاولة الأولى (معيبة):
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)
}
}
تحرير 1:
ما سبق ليس قويًا بما فيه الكفاية (لا يمكنك إضافة اثنين Plus
التعبيرات) ، والشاهد الضمني لا يلزم تحديده داخل Plus
فئة الحالة ... جرب هذا بدلاً من ذلك:
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)
}
تحرير 2:
إليك نسخة (ربما) أكثر قليلاً:
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)
}
نصائح أخرى
فيما يلي التنفيذ الكامل لمشكلة التعبير في Scala باستخدام فئات النوع
trait Exp
case class Lit(value: Int) extends Exp
case class Add[A <: Exp, B <: Exp](left: A, right: B) extends Exp
فئة نوع التقييم والتطبيقات الضمنية
//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)
}
يتيح تمديد الحل عن طريق إضافة نوع جديد يسمى 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)
}
الآن يمكن تقييم التعبيرات مثل هذا
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))
}
يتيح تمديد النظام عن طريق إضافة طباعة عملية جديدة
//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) }
}
حدد طريقة جديدة لطباعة التعبيرات
def printExpressions[A <: Exp](exp : A)(implicit p : Print[A]) = {
p.print(exp)
}
قم بتحديث الطريقة الرئيسية لطباعة التعبير
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))
}
يمكن تنفيذ الحل بأكمله عن طريق لف الرمز داخل كائن.