implicit ground environment in make-environment?

24 views
Skip to first unread message

Mariano Guerra

unread,
Jul 2, 2012, 9:45:12 AM7/2/12
to kl...@googlegroups.com
hi, in section 4.8.4 it says:

4.8.4 make-environment

(make-environment . environments)

The applicative constructs and returns a new environment, with
initially no local
bindings, and parent environments the environments listed in environments. The
constructed environment internally stores its list of parents
independent of the first-
class list environments, so that subsequent mutation of environments
will not change
the parentage of the constructed environment. If the provided list
environments is
cyclic, the constructed environment will still check each of its
parents at most once,
and signal an error if no binding is found locally or in any of the parents.

==============

in there it doesn't make any reference to the ground environment, in
klisp if I run:

klisp> (eval (list cons 1 2) (make-environment))
(1 . 2)
klisp> (($vau (expr) #ignore (eval expr (make-environment))) (cons 1 2))

*ERROR*:
eval: Unbound symbol: (cons)

what is the difference between the two?

since eval is an applicative I think (list cons 1 2) will be
evaluated to (cons 1 2) and passed to eval.

the second one seems to do the same (as far as I understand) but fails
not founding cons.

are they equivalent? if not, why?

I started this mail asking if make-environment created an environment
with the ground environment implicitly as parent and now I'm more
confused :P

Andres Navarro

unread,
Jul 2, 2012, 10:39:16 AM7/2/12
to kl...@googlegroups.com

Your second example does what you are trying to do, and the answer is that no, make-environment doesn't add an implicit ground environment as parent.  There are probably a number of reasons for this, but the most important one is that as it is the only environment constructor, it should be able to create completely empty environments (which are very useful btw).

Regarding your question about the difference between the two expressions, the first one "works" because the list you are constructing is not (cons 1 2) but actually (#primitive-applicative:cons 1 2).  What I mean by that is that the list doesn't contain the symbol cons (which would be bound in the created environment) but the actual applicative cons.  The "correct" way to do what you are trying to do is:
(eval ((unwrap list) cons 1 2) (make-environment))

or

($define! $quote ($vau (x) #ignore x))
(eval (list ($quote cons) 1 2) (make-environment))

This trips people every time when trying to understand the behaviour of applicatives, operatives and environments.  The easiest way to be sure is to see (e.g. by using write or display) the list you are evaluating, and see if you have symbols or other objects in your list.

Regards,
Andres Navarro

Dale Schumacher

unread,
Jul 2, 2012, 12:54:37 PM7/2/12
to kl...@googlegroups.com
On Mon, Jul 2, 2012 at 8:45 AM, Mariano Guerra
<luismari...@gmail.com> wrote:
> in there it doesn't make any reference to the ground environment, in
> klisp if I run:
>
> klisp> (eval (list cons 1 2) (make-environment))
> (1 . 2)
> klisp> (($vau (expr) #ignore (eval expr (make-environment))) (cons 1 2))
>
> *ERROR*:
> eval: Unbound symbol: (cons)
>
> what is the difference between the two?
>
> since eval is an applicative I think (list  cons 1 2) will be
> evaluated to (cons 1 2) and passed to eval.
>
> the second one seems to do the same (as far as I understand) but fails
> not founding cons.
>
> are they equivalent? if not, why?
>
> I started this mail asking if make-environment created an environment
> with the ground environment implicitly as parent and now I'm more
> confused :P

"make-environment" only includes/inherits-from environments explicitly listed.

I think your confusion is based on misunderstanding what is evaluated
when and in what environment. This is an area I struggled with
myself. It's kind of like the game of "Go". The rules are simple,
but the implications can get complicated in a hurry!

Your first example, "(eval (list cons 1 2) (make-environment))", is
recognized as a list/pair, so the "car" (which is the symbol "eval")
is evaluated in the dynamic environment. If this is the ground
environment, then the symbol "eval" is bound to the applicative
/eval/. Since /eval/ is applicative, the "cdr" (which is "((list cons
1 2) (make-environment))") forms a list of (two) operands, that are
each evaluated (in the environment of the call).

The tricky bit is that "(list cons 1 2)" evaluates to "(/cons/ 1 2)",
where /cons/ represents the applicative object bound to the symbol
"cons" in the dynamic environment. The operative inside /eval/ is
passed a list of two elements, which by now contain *no symbols*. The
first element is "(/cons/ 1 2)" and the second is an empty
environment. The applicative /cons/ is not a symbol, so it is not
"looked up" in the (empty) environment, it is just applied to the
operands "(1 2)". Since /cons/ is applicative, the operands are each
evaluated (in the empty environment) to produce the arguments "(1 2)".
The environment doesn't matter here because the constants evaluate to
themselves.

I hope this provides more clarity rather than increasing confusion.
The process is intricate, but ultimately elegant and powerful.
Reply all
Reply to author
Forward
0 new messages