Circular references between classes in Scala

2019-09-08 00:31发布

问题:

Here is the code:

  trait Simplifier {
    def caseNum(t: Num) = {
      t.value
    }
    def caseMul(t: Mul) = {
      t.right.simplify(this) * t.left.simplify(this)
    }
    def caseVar(t: Var) = {
      t.assigned.simplify(this)
    }
  }

  trait Expr {
    def simplify(s: Simplifier): Int
  }

  class Num(val value: Int) extends Expr {
    def simplify(s: Simplifier) = {
      s.caseNum(this)
    }
  }

  class Var(val assigned: Expr) extends Expr {
    def simplify(s: Simplifier) = {
      s.caseVar(this)
    }
  }

  class Mul(val left: Expr, val right: Expr) extends Expr {
    def simplify(s: Simplifier) = {
      s.caseMul(this)
    }
  }

If you paste this into the repl, you will get

<console>:11: error: not found: type Num
           def caseNum(t: Num) = {
                          ^
<console>:14: error: not found: type Mul
           def caseMul(t: Mul) = {
                          ^
<console>:17: error: not found: type Var
           def caseVar(t: Var) = {
                          ^

If you change the order of definitions, you still will get similar errors.

回答1:

If you use :paste at the REPL this compiles fine. Without the :paste command, the evaluation is happening immediately, the classes that are not yet defined cannot be seen so it fails. In paste mode, they are compiled together.

See below:

scala> :paste
// Entering paste mode (ctrl-D to finish)

 trait Simplifier {
    def caseNum(t: Num) = {
      t.value
    }
    def caseMul(t: Mul) = {
      t.right.simplify(this) * t.left.simplify(this)
    }
    def caseVar(t: Var) = {
      t.assigned.simplify(this)
    }
  }

  trait Expr {
    def simplify(s: Simplifier): Int
  }

  class Num(val value: Int) extends Expr {
    def simplify(s: Simplifier) = {
      s.caseNum(this)
    }
  }

  class Var(val assigned: Expr) extends Expr {
    def simplify(s: Simplifier) = {
      s.caseVar(this)
    }
  }

  class Mul(val left: Expr, val right: Expr) extends Expr {
    def simplify(s: Simplifier) = {
      s.caseMul(this)
    }
  }

// Exiting paste mode, now interpreting.

defined trait Simplifier
defined trait Expr
defined class Num
defined class Var
defined class Mul


回答2:

An alternative to using the :paste command, is manually wrapping your code in a wrapper object.

scala> object Wrapper {
     |   trait Simplifier {
     |     def caseNum(t: Num) = {
     |       t.value
     |     }
     |     def caseMul(t: Mul) = {
     |       t.right.simplify(this) * t.left.simplify(this)
     |     }
     |     def caseVar(t: Var) = {
     |       t.assigned.simplify(this)
     |     }
     |   }
     | 
     |   trait Expr {
     |     def simplify(s: Simplifier): Int
     |   }
     | 
     |   class Num(val value: Int) extends Expr {
     |     def simplify(s: Simplifier) = {
     |       s.caseNum(this)
     |     }
     |   }
     | 
     |   class Var(val assigned: Expr) extends Expr {
     |     def simplify(s: Simplifier) = {
     |       s.caseVar(this)
     |     }
     |   }
     | 
     |   class Mul(val left: Expr, val right: Expr) extends Expr {
     |     def simplify(s: Simplifier) = {
     |       s.caseMul(this)
     |     }
     |   }
     | }
defined module Wrapper

scala> import Wrapper._
import Wrapper._


标签: scala