Help me write my LISP environments :) LISP, Ruby Hashes

I am implementing a rudimentary version of LISP in Ruby just to familiarize myself with some concepts. I base my performance on Peter Norwig Lisp (http://norvig.com/lispy.html).

There is something missing here, and I would be happy to help ...

It subclasses the Python dict as follows:

class Env(dict): "An environment: a dict of {'var':val} pairs, with an outer Env." def __init__(self, parms=(), args=(), outer=None): self.update(zip(parms,args)) self.outer = outer def find(self, var): "Find the innermost Env where var appears." return self if var in self else self.outer.find(var) 

He then goes on to explain why he is doing this, and not just using a dict. However, for some reason, his explanation continues to go through my eyes and go out through the back of my head.

Why not use a dict, and then inside the eval function, when you need to create a new "sub-environment", just take the existing dict and update the key / value pairs that need to be updated and pass that new dict to the next eval?

Will the Python interpreter keep track of previous "external" envs? And will the nature of the recursion guarantee that the values ​​are derived from "internal" to "external"?

I use Ruby and I tried to implement things this way. Something does not work, although perhaps because of this, or maybe not. Here is my eval function, which is a regular hash:

 def eval(x, env = $global_env) ........ elsif x[0] == "lambda" then ->(*args) { eval(x[2], env.merge(Hash[*x[1].zip(args).flatten(1)])) } ........ end 

A line that, of course, has the meaning of "lambda."

If there is a functional difference, what is significantly different from what I'm doing here and what did Norvig do with his Env class? Can someone describe to me the case when two will deviate?

If there is no difference, then maybe someone can tell me why Norwig uses the Env class. Thanks:)

+7
source share
2 answers

If the variable bindings in your Lisp are immutable, copy the environments equivalent to binding them. But consider the following case:

 (define main (lambda () (define i 0) (define increment-i (lambda () (set! i (+ i 1)))) (increment-i) i)) (main) 

If the increment-i environment is completely independent of main (since this is a copy), the mutation i will not be visible in main , and the above code will return 0 . If, on the other hand, the environments are connected, the return value will be 1 , as expected.

+5
source

My Python is not so good, and my Lisp is pretty rusty, but I will think about what is happening: you cannot get away from pointers even in languages ​​that claim to not have them.

eval you are talking about will not make a copy of the dict , it will just make a copy of the link, and you will get two links (AKA variables) that reference (i.e. indicate) the same base object. This is basically an option:

 a = [ 'a', 'b', 'c' ] b = a a[1] = 'x' puts a # => ["a", "x", "c"] puts b # => ["a", "x", "c"] 

Its Env class allows you to change the environment inside eval without changing the external environment, while the find method allows you to access the external environment without the need to know; In addition, the changes will be inside the internal environment, and these changes will mask the corresponding values ​​in the external environment; get operations will access the local environment and the external environment, operations with settings will only change the internal environment.

I think you could call Env an object-level facade (rather than a class level).

I'm not sure what happened to your ruby ​​implementation, it looks like you are making a modified copy of the environment hash. Can you explain "Something is not working"?

Specification: Env is a character table. The find / wrap material in find allows you to access the external character table, protecting it from adding new characters, new characters will only be added to the internal character table. Env essentially manages the closure.

+1
source

All Articles