Тип классов в Scala
Наличие фона в Haskell В настоящее время я пытаюсь познакомиться с Scala.
Я столкнулся с некоторыми проблемами, пытающимися перевести малый язык расширяемых выражений из 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 переводятся в концептуальный шаблон с implicits в 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, однако меня интересует эта версия в частности.
Спасибо, что прочитали мой довольно длительный вопрос.
Ответы
Ответ 1
Первая попытка (ошибка):
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
), и неявный свидетель не должен быть определен внутри класса case 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)
}
Ответ 2
Вот полная реализация проблемы с выражением в scala с использованием классов типов
trait Exp
case class Lit(value: Int) extends Exp
case class Add[A <: Exp, B <: Exp](left: A, right: B) extends Exp
Класс Eval и неявные реализации
//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))
}
Позволяет расширить систему, добавив новую операцию Print
//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))
}
Полное решение может быть выполнено путем переноса кода внутри объекта.