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.

È stato utile?

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.

scroll top