سؤال

امتلاك خلفية في 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))
}

يمكن تنفيذ الحل بأكمله عن طريق لف الرمز داخل كائن.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top