[racket] help: how to make eval see local bindings?

67 views
Skip to first unread message

Maurizio Giordano GMAIL

unread,
Jul 11, 2011, 8:26:19 AM7/11/11
to users
Hi to all schemers,

I know that "eval" evaluates the argument without
visibility of the local environment where it is called.
so the following code has this error:

> (let ((x 1)) (eval '(+ x 1))
reference to undefined identifier: x

On contrary, if "x" is defined in the top environment, I have:

> (define x 2)
> (let ((x 1)) (eval '(+ x 1))
4

So the eval sees the global environment.

Can someone tell me if it possible to evaluate a
quoted expression in such a way all local bindings
(the "let" bindings" are visible to the eval?

In my case I need to eval a quoted lambda which uses
variables that "should" bound in the local environment ...
this is the code:

> (let ((x 1)
(f (eval '(lambda (y) (+ x y)))))
(f 2))
reference to undefined identifier: x

PS. my lambda is generated by a macro (define-syntax) ...
this is why I use eval to generate the corresponding procedure.

TIA,

Maurizio Giordano


_________________________________________________
For list-related administrative tasks:
http://lists.racket-lang.org/listinfo/users

Noel Welsh

unread,
Jul 11, 2011, 8:43:40 AM7/11/11
to Maurizio Giordano GMAIL, users
On Mon, Jul 11, 2011 at 1:26 PM, Maurizio Giordano GMAIL
<maurizi...@gmail.com> wrote:
> PS. my lambda is generated by a macro (define-syntax) ...
> this is why I use eval to generate the corresponding procedure.

If this is the case I don't think you need to use eval. You either
need to write your macro in a hygenic way -- it accepts as parameters
all the identifiers in the enclosing environment that is should
reference -- or you should explicitly break hygiene. If you're new-ish
to Racket the preceeding sentence probably won't make any sense, so
feel free to ask for more help!

Cheers,
N.

Hendrik Boom

unread,
Jul 11, 2011, 9:02:08 AM7/11/11
to us...@racket-lang.org
On Mon, Jul 11, 2011 at 02:26:19PM +0200, Maurizio Giordano GMAIL wrote:
> Hi to all schemers,
>
> I know that "eval" evaluates the argument without
> visibility of the local environment where it is called.
> so the following code has this error:
>
> > (let ((x 1)) (eval '(+ x 1))
> reference to undefined identifier: x
>
> On contrary, if "x" is defined in the top environment, I have:
>
> > (define x 2)
> > (let ((x 1)) (eval '(+ x 1))
> 4
>
> So the eval sees the global environment.
>
> Can someone tell me if it possible to evaluate a
> quoted expression in such a way all local bindings
> (the "let" bindings" are visible to the eval?
>
> In my case I need to eval a quoted lambda which uses
> variables that "should" bound in the local environment ...
> this is the code:
>
> > (let ((x 1)
> (f (eval '(lambda (y) (+ x y)))))
> (f 2))
> reference to undefined identifier: x
>
> PS. my lambda is generated by a macro (define-syntax) ...
> this is why I use eval to generate the corresponding procedure.

I'm not sure what you're trying to do, but leaving out both the eval and the
quote would seem to be what you want:


(let ((x 1)
(f (lambda (y) (+ x y))))
(f 2))

I don't know how that relates to your macro.

-- hendrik

J. Ian Johnson

unread,
Jul 11, 2011, 10:12:52 AM7/11/11
to Hendrik Boom, us...@racket-lang.org
I'd like to add a +1 to the original question. If I can get eval to see local bindings, I can write a gdb-like debugger for Racket using call/cc, read and eval.
-Ian

Maurizio Giordano GMAIL

unread,
Jul 11, 2011, 11:04:24 AM7/11/11
to Noel Welsh, users
On Mon, 2011-07-11 at 13:43 +0100, Noel Welsh wrote:
> On Mon, Jul 11, 2011 at 1:26 PM, Maurizio Giordano GMAIL
> <maurizi...@gmail.com> wrote:
> > PS. my lambda is generated by a macro (define-syntax) ...
> > this is why I use eval to generate the corresponding procedure.
>
> If this is the case I don't think you need to use eval. You either
> need to write your macro in a hygenic way -- it accepts as parameters
> all the identifiers in the enclosing environment that is should
> reference -- or you should explicitly break hygiene. If you're new-ish
> to Racket the preceeding sentence probably won't make any sense, so
> feel free to ask for more help!
>
> Cheers,
> N.

Hi Nole,
Yes, I am an absolute new-ish to racket... I would like to know more
about writing a macro by "explicitly break hygiene".
I supposed I didn't need to use eval... but for me it is
the easiest way to "build" a lambda-code and then evaluate it.

I want to give you more details about my problem.
I have a macro like this:

(define-syntax mymacro
(syntax-rules (mymacro)
[(mymacro input ...)
(let* (...)
(eval `(lambda (cntx)
... ; a code generated according to macro
"input ..."
(+ x 2) ; that contains references to an outside "x"
symbol
...
))]))

Note that I cannot foresee which extern symbols are referenced by the
generated lambda.
This macro returns a procedure.
if I use my macro in a "let" construct, of course I have a "reference to
an undefined identifier":

(let* ((x 1)
(f (mymacro ...)))


(f 2))
reference to undefined identifier: x

So, coming back to my question: since the eval is called within the
"let"
scope, is it possible to make it aware of the bindings of the "let"?
Do you know alternative mechanism to do that?

TIA,
Cheers,

Maurizio.

Matthias Felleisen

unread,
Jul 11, 2011, 11:26:49 AM7/11/11
to Maurizio Giordano GMAIL, users

This sounds a bit confused. Allow me to tease out a clarification.

1. If you write a macro like this:

(define-syntax mym
(syntax-rules ()
[(_ input ...)
(let* ([x 10])
(lambda (stuff)
input ... ;; line 2
x))]))

'hygiene' gives you a couple of different things:

-- the x and the stuff introduced in the macro doesn't accidentally interfere with free variables in input ... on line 2

-- the let* and the lambda are guaranteed to refer to the meaning in place where you define the macro


2. So when you run this expression in the context of mym

(let ([x 42]) ;; line 9
((mym (displayln x)) ;; line 7
'random-argumnent))

you see

-- the output 42 from the displayln on line 7
-- the result 10 from the line below line 2


3. It is rare that mym needs to generate code involving the x on line 9 WITHOUT specifying the x in an input position for mym.

QUESTION: can you explain why you'd want to do so?
POSSIBLE ANSWER: you may wish to break hygiene then.

4. It is equally rare that you want let* or lambda to mean what the context of the macro call says they are.

IF THIS IS WHAT YOU WANT, can you provide more details.

maurizio.giorda

unread,
Jul 11, 2011, 12:27:57 PM7/11/11
to Matthias Felleisen, users
Ok, I will give you more details about my project... maybe you could be interested
in it :>
But first I need to underline that the macro *cannot foresee* which extern variables the
lambda can reference ... so I cannot adopt your solution:
(define-syntax mym  
  (syntax-rules ()
    [(_ input ...)
     (let* ([x 10])    ; at this time, I don't know if lambda will use "x", if "x" is bounded   
                       ; and (if bounded) which is the binding
 (lambda (stuff)
         input ...        ;; line 2 
         x))]))
Now let's describe my project.
I have defined a new language in racket, named chemical language (HOCL).
The basic data structure of HOCL is the "multiset". It is a similar to a vector,
with the difference that the elements are not ordered, but they move stochastically
inside the vector (but this is all folks!). The multiset is a sort of chemical container
(solution) of molecules.

molecules can be any racket object: numbers?, strings, lists, and "gamma-rules".
A "gamma-rule" is very similar to a scheme lambda. It is a function
that captures some elements (chemical molecules) of the multiset and produces
new molecules. In the chemical language, gamma-rules are the "chemical rules"
that fire reactions in the mutliset (the chemical solution), and that transform molecules in
new molecules.

I implemented a reader for the multiset notation: < molecule1, molecule2, ... >
where commas separates the multiset elements, and "<" ">" are  multiset delimiters.

I create and bind a multiset in this way

(define m < 1,2,3,4,(replace x y by (+ x y)), 5, 6 >)  ; top level definition
< 1,2,3,4, #<procedure>, 5, 6 >                          
; the output is a chemical container with a gamma-rule inside it (plus other molecules)
; the "x" and "y" free variables of the rule are bound to molecules by a pattern matching
mechanism,

Now I have a chemical engine ("cham") that starts reactions in the chemical solution.
The gamma-rules are applied to the mutliset (with the sheme "apply").
> (cham m)
< 21, #<procedure> >

As you see the chemical rule (gamma-rule) applies many times, and each time it adds
pairs of molecules until one molecule (the total sum) is left (the chemical inert state).

Now let's talk about the "gamma-rule" generation: the macro "replace ... by ... "
generate the gamma-rule code
(define-syntax replace
 (syntax-rules (replace)
   [(replace input ...)
    (let* (...)
      (eval `(lambda (cntx)  ; the code of the "gamma-rule"
                ...      
             ))]))

Now the problem... In the previous example I can define a rule like this:

(replace x y by (+ x y) if (> top (+ x y))), 5, 6 >)

that is, I want the reaction to take place only if the partial sum of molecules is not over "top",
where the "top" variable may be externally defined.
Now I can write the code:

(let ((top 8)
       (m < 1,2,3,4,(replace x y by (+ x y) if (> top (+ x y))), 5, 6 >)))   ; multiset elements are evaluated after reading
  (cham m))
reference to undefined identifier: top

... I hope now it is clear that I need to generate procedures with references to symbols that can
(or cannot) have bindings when the procedure is called... in fact, the user writes
the (replace ....) form but the system cannot foresee which extern symbols the generated procedure
may use.

Thank you for your attention.
Sorry for a so long email... I couldn't explain my probelm with less details.
Maurizio.

Matthias Felleisen

unread,
Jul 11, 2011, 2:15:54 PM7/11/11
to maurizio.giorda, users

Now I don't understand at all why you want to use eval in the macro.
If the right-hand side just returned the lambda that you have there,
it would automatically capture the variables in the context of the
gama rule.

Have you tried just returning the lambda as is from the macro?

> <btn_viewmy_160x25.png>

Maurizio Giordano

unread,
Jul 12, 2011, 6:41:34 AM7/12/11
to users
Hi Matthias, hi schemers,

2011/7/11 Matthias Felleisen <matt...@ccs.neu.edu>


Now I don't understand at all why you want to use eval in the macro.
If the right-hand side just returned the lambda that you have there,
it would automatically capture the variables in the context of the
gama rule.

 
Have you tried just returning the lambda as is from the macro?

I tried but it doesn't work! Here you find a sample of what you suggest:
---------------
(define-syntax mymacro
  (syntax-rules (prova)
    [(mymacro input ...)
     (lambda (z) (list input ...) (+ z extern-var))]))

> (let* ((extern-var 2) (f (mymacro x y))) (f 1))
reference to undefined identifier: extern-var
-----------------
So, it seeems that although I avoid "eval" the problem
is still there!

Just to give you more details of my implementation... 
In my case things are a little more complicated:

I told you that the gamma-rule is "similar" to a lambda, not a lambda.
In order to separate gamma-rules form normal procedures I defined
a structure for gamma-rules like this:

  ; Rule datatype definition
  (define-values (s:gamma make-gamma gamma? gamma-ref gamma-set!)
     (make-struct-type 'gamma #f 3 0 #f ...))

the first field is the procedure resulted form the lambda evaluation (it is the lambda generated by the "replace" macro, and the lambda code generation depends on the gamma-rule inputs), the other fields are parameters of the rule, like "active" or "inactive", etc.

so the "replace" macro does not return a lambda, but a struct whose 
first field is a procedure resulting from: (eval `(lambda ...)):

(define-syntax replace-once 
   (syntax-rules (replace-once)                
        [(replace input ...)
         (let* (...compute some parameters "x" "y" from input...)
           (make-gamma 
             (eval 
                (quasiquote                  
                  (lambda (cntx) 
                     ...unquote "x" and "y" to generate specific code ...
                     ...some references to external symbols ... 
                  )))
             <field2>
             <filed3>))]))

I know that my implementation seems to be complicated,
but, since I have a "new language" running on scheme, I would like to
have multisets and (chemical) gamma rules defined as new structures different from similar native structures of racket (i.e. vectors and procedures).

From what you say I understand I HAVE TO AVOID EVAL...
... and up to now I don't find another solution to generate the
lambda, other than "(eval (quasiquote (lambda (...) ...))".

Thank you for your attention.
If you mind some "eval" alternatives that are able to capture
local binding (i.e. the local environment), please let me know.
Browsing the racket documentation I found some alternative "eval"
version (r5rs):

(eval expr evironment-specific)

but it seems it allows only to capture a specified namespace
bunding... I will spend some time to look at it!

Cheers,

Maurizio. 

Matthias Felleisen

unread,
Jul 12, 2011, 9:04:03 AM7/12/11
to Maurizio Giordano, users
On Jul 12, 2011, at 6:41 AM, Maurizio Giordano wrote:

Hi Matthias, hi schemers,

2011/7/11 Matthias Felleisen <matt...@ccs.neu.edu>

Now I don't understand at all why you want to use eval in the macro.
If the right-hand side just returned the lambda that you have there,
it would automatically capture the variables in the context of the
gama rule.

 
Have you tried just returning the lambda as is from the macro?

I tried but it doesn't work! Here you find a sample of what you suggest:
---------------
(define-syntax mymacro
  (syntax-rules (prova)
    [(mymacro input ...)
     (lambda (z) (list input ...) (+ z extern-var))]))

> (let* ((extern-var 2) (f (mymacro x y))) (f 1))
reference to undefined identifier: extern-var

Here is what I meant: 

#lang racket

(define-syntax-rule
  (mm x)
  (lambda (w) (displayln `(,x)) (+ w x)))

(define f 
  (let ([a 10])
    (mm a)))

(f 42)
  
More later -- Matthias

maurizio.giorda

unread,
Jul 12, 2011, 9:36:06 AM7/12/11
to Matthias Felleisen, users
Hi Matthias,

... I will play with your example (it is better than all the words I spent with my looong
description:

Here is what I meant: 

#lang racket

(define-syntax-rule
  (mm x)
  (lambda (w) (displayln `(,x)) (+ w x)))
Ok, fine!


(define f 
  (let ([a 10])
    (mm a)))

(f 42)
Let me define my function in a inner scope:

> (let ((othervar 2)
          (f (mm (+ 1 othervar))))
>  (f 1) 

it should print 4 but it gives the undefined identifier error.
If I define "othervar" globally (top env) it works (of course!).

> (define othervar 2)
>  (let ((f (mm (+ 1 othervar))))
        (f 1)
it prints 4.

If you ask me why I need to call "mm" with an expression, instead of a value... like: (mm (+ 1 othervar))
The answer is: my macro "replace" is a constructor of a function... I would like that this
function could reference external variables (not passed by parameters): if the reference
extern variable is defined (in the current scope) its value is used, otherwise the function
must give the "undefined identifier error".
The function I refer to is a "chemical reaction function": it applies to chemical molecules
(the macro parameters) but it can use reference to external variables to fire or not the
reaction. Example

(replace x y by x if (> x top)) -> #<procedure>

says: replace any pair of two molecules (x and y) with only one of them if
the value of the first is not greater than "top".

Cheers,

Maurizio.

Markku Rontu

unread,
Jul 12, 2011, 10:11:24 AM7/12/11
to maurizio.giorda, users
On Tue, Jul 12, 2011 at 4:36 PM, maurizio.giorda <maurizi...@gmail.com> wrote:
Hi Matthias,

... I will play with your example (it is better than all the words I spent with my looong
description:

Here is what I meant: 

#lang racket

(define-syntax-rule
  (mm x)
  (lambda (w) (displayln `(,x)) (+ w x)))
Ok, fine!


(define f 
  (let ([a 10])
    (mm a)))

(f 42)
Let me define my function in a inner scope:

> (let ((othervar 2)
          (f (mm (+ 1 othervar))))
>  (f 1) 

it should print 4 but it gives the undefined identifier error.
If I define "othervar" globally (top env) it works (of course!).


Do you want a let* here instead of let?

-Markku

maurizio.giorda

unread,
Jul 12, 2011, 11:00:54 AM7/12/11
to Markku Rontu, users
Yes Markku,
you (and Matthias) are absolutely right... with let* works.
I did a mistake like a very beginner...

Nevertheless, now I have to re-formulate my macro.
I started from something like:


(define-syntax replace
   (syntax-rules (replace)
     [(replace input ...)
      (let* (...let-variables ...)
        (eval `(lambda (cntx) 
                  ...  here unquote either let-variables and other utility functions...
                  ... maybe reference external variables...   
              ))]))

What I do is simply to build the lambda code (with quasiquote  ... unquote ...).
The code is huge. It "replace" macro is some sort of compiler that generates the scheme code
for a new construct in a new language (a chemical language) implemented on top of racket.

Since Matthias suggested to avoid the (eval (quasiquote (lambda ...))) approach and replace it
with a (lambda ... (quasiquote ...)), now my work is trying to redesign my implementation.

Thank you,
Cheers,

Maurizio.
Reply all
Reply to author
Forward
0 new messages