Handling recursion in scala's scheme interpreter

198 Views Asked by At

I'm giving the following code that implements an interpreter of scheme language from scala. The relevant cases are those that contain a call to updateEnvRec or updateEnv:

 def evalRec(x: Data, env: Env): Data = {
    x match {
      case i: Int => i
      case Symbol(s) => env(s) match {
        case Some(v) => v
        case None => sys.error("Unknown symbol " + s)
      }
      case List('lambda, params: List[Data], body) =>
        ((args: List[Data]) => {
          val paramBinding = params.map(_.asInstanceOf[Symbol].name).zip(args)
          evalRec(body, updateEnv(env, paramBinding))
        })
      case List('val, Symbol(s), expr, rest) =>
        evalRec(rest, updateEnv(env, List(s -> evalRec(expr, env))))
      case List('def, Symbol(s), expr, rest) => {
        evalRec(rest, updateEnvRec(env, s, expr))
      }
      case List('if, bE, trueCase, falseCase) =>
        if (evalRec(bE, env) != 0) evalRec(trueCase, env)
        else evalRec(falseCase, env)
      case opE :: argsE => {
        val op = evalRec(opE, env).asInstanceOf[List[Data] => Data]
        val args: List[Data] = argsE.map((arg: Data) => evalRec(arg, env))
        op(args)
      }
    }
  }

The aim of this code is handle expressions such as:

(val factorial (lambda (x)
(if (= x 0) 1 (* x (factorial (- x 1)))))
(factorial 6))

To do so I'm given the following updates and definition of environment:

 type Env = String => Option[Data]
  val recEnv : Env = ((id:String) => funEnv.get(id)) 
  def updateEnv(env : Env, bindings : List[(String,Data)]) : Env = bindings match {
    case Nil => env
    case (id,d)::rest => ((x:String) =>
        if (x==id) Some(d)
        else updateEnv(env,rest)(x))
  }
  def updateEnvRec(env: Env, s : String, expr : Data) : Env = {
    def newEnv : Env = ((id:String) =>
      if (id==s) Some(evalRec(expr, newEnv))
      else env(id)
    )
    newEnv
  }  

My problem is that I don't understand why calling:

 evalRec(rest, updateEnv(env, List(s -> evalRec(expr, env))))

works. I'd rather write:

evalRec(rest, updateEnvRec(env,s,expr))

because with the first strategy I would have to evaluate first evalRec(expr, env) and this would result in an error because at that point env does not contain factorial. What am I missing?

1

There are 1 best solutions below

0
On BEST ANSWER

I believe your example

(val factorial 
  (lambda (x)
    (if (= x 0) 
        1 
        (* x (factorial (- x 1)))))
(factorial 6))

is wrong. If I understand the code correctly, val doesn't introduce an recursive binding, therefore the the inner factorial is unbound. And you interpreter gives you just that error.

Instead try this program:

(def factorial 
  (lambda (x)
    (if (= x 0) 
        1 
        (* x (factorial (- x 1)))))
(factorial 6))