1
votes

I am doing a tutorial about Scala (https://docs.scala-lang.org/tutorials/scala-for-java-programmers.html) and there is an example how to use case classes and pattern matching.

I am using the IDE IntelliJ IDEA.

I tried that example and I have an abstract class Tree with the case classes Sum, Var and Const.

In the tutorial there are the methods eval and derive implemented.

There is also a main method showing the functionality of the class.

There is: You will need to wrap the Environment type and eval, derive, and main methods in a Calc object before compiling. Executing this program, we get the expected Output...

I put the type Environment and the methods eval, derive and main into an object called Calc. But then there were Sum, Var and Const not resolved. So I added exp. in front of that variables (exp.Sum, exp.Var, exp.Const) exp. is the variable of the tree class

According to the tutorial there is no exp in front of that case classes and my IDE cannot find them if I leave out exp before that case classes calls in the main method.

When I put exp. (variable of the tree object) to try to access the case classes then the error message "Forward reference extends over Definition of value exp" appears.

What I am doing wrong?

The tutorial does not say how to access case classes of an abstract class correctly and the code of the tutorial (main method) does not work.

The abstract class is:

abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree
case class Var(n: String) extends Tree
case class Const(v: Int) extends Tree

The Environment and eval method are:

type Environment = String => Int

def eval(t: Tree, env: Environment): Int = t match {
  case Sum(l, r) => eval(l, env) + eval(r, env)
  case Var(n)    => env(n)
  case Const(v)  => v
}

The derive method is:

def derive(t: Tree, v: String): Tree = t match {
  case Sum(l, r) => Sum(derive(l, v), derive(r, v))
  case Var(n) if (v == n) => Const(1)
  case _ => Const(0)
}

The main method is:

def main(args: Array[String]): Unit = {
  val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y")))
  val env: Environment = { case "x" => 5 case "y" => 7 }
  println("Expression: " + exp)
  println("Evaluation with x=5, y=7: " + eval(exp, env))
  println("Derivative relative to x:\n " + derive(exp, "x"))
  println("Derivative relative to y:\n " + derive(exp, "y"))
}

The entire code:


abstract class Tree {

  case class Sum(l: Tree, r: Tree) extends Tree
  case class Var(n: String) extends Tree
  case class Const(v: Int) extends Tree

  type Environment = String => Int

  def eval(t: Tree, env: Environment): Int = t match {
    case Sum(l, r) => eval(l, env) + eval(r, env)
    case Var(n)    => env(n)
    case Const(v)  => v
  }

  def derive(t: Tree, v: String): Tree = t match {
    case Sum(l, r) => Sum(derive(l,v),derive(r,v))
    case Var(n) if (v == n) => Const(1)
    case _ => Const(0)
  }

}

object Calc {



  def main(args: Array[String]): Unit = {
    val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(exp.Const(7),Var("y")))
    val env: Environment = {
      case "x" => 5
      case "y" => 7
    }

    println("Ausdruck: " + exp)
    println("Auswertung mit x=5, y=7: " + exp.eval(exp, env))
    println("Ableitung von x:\n " + exp.derive(exp, "x"))
    println("Ableitung von y:\n " + exp.derive(exp, "y"))

  }

}

Here Sum, Var, Environment can not be found by the IDE. If I write exp.Sum and exp.Var the other error message appears.

I do not understand how to use that example of the tutorial correctly. How I can get this to work?

1
This code compiles OK, so you need to show the code that doesn't work, including the containing objects. - Tim
Start by declaring the case classes outside the abstract class Tree, eventually declare Tree as a trait and not as an abstract class. - Catalina Chircu

1 Answers

1
votes

The straightforward solution is to put the Tree code in an object and import it:

object Expr {
  trait Tree
  case class Sum(l: Tree, r: Tree) extends Tree
  case class Var(n: String) extends Tree
  case class Const(v: Int) extends Tree

  type Environment = String => Int

  def eval(t: Tree, env: Environment): Int = t match {
    case Sum(l, r) => eval(l, env) + eval(r, env)
    case Var(n)    => env(n)
    case Const(v)  => v
  }

  def derive(t: Tree, v: String): Tree = t match {
    case Sum(l, r) => Sum(derive(l,v),derive(r,v))
    case Var(n) if (v == n) => Const(1)
    case _ => Const(0)
  }
}

object Calc {
  import Expr._

  def main(args: Array[String]): Unit = {
    val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y")))
    val env: Environment = {
      case "x" => 5
      case "y" => 7
    }

    println("Ausdruck: " + exp)
    println("Auswertung mit x=5, y=7: " + eval(exp, env))
    println("Ableitung von x:\n " + derive(exp, "x"))
    println("Ableitung von y:\n " + derive(exp, "y"))
  }
}

But it is much cleaner to add the appropriate methods directly to each case class rather than unpicking them in eval and derive:

object Expr {
  type Environment = String => Int

  trait Tree {
    def eval(env: Environment): Int
    def derive(v: String): Tree
  }
  case class Sum(l: Tree, r: Tree) extends Tree {
    def eval(env: Environment): Int = l.eval(env) + r.eval(env)
    def derive(v: String): Tree = Sum(l.derive(v), r.derive(v))
  }
  case class Var(n: String) extends Tree {
    def eval(env: Environment): Int = env(n)
    def derive(v: String): Tree = if (n == v) Const(1) else Const(0)
  }
  case class Const(v: Int) extends Tree{
    def eval(env: Environment): Int = v
    def derive(v: String): Tree = Const(0)
  }
}

object Calc {
  import Expr._

  def main(args: Array[String]): Unit = {
    val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y")))
    val env: Environment = {
      case "x" => 5
      case "y" => 7
    }

    println("Ausdruck: " + exp)
    println("Auswertung mit x=5, y=7: " + exp.eval(env))
    println("Ableitung von x:\n " + exp.derive("x"))
    println("Ableitung von y:\n " + exp.derive("y"))
  }
}