[racket] obtaining the location of an identifier

From: Sam Tobin-Hochstadt (samth at cs.indiana.edu)
Date: Wed Nov 20 13:28:59 EST 2013

Two things:

- There's code here [1] to find all the mutated variables in a module.
- Robby's later suggestion is great if you want to track _all_
variables, not just ones that are targets of `set!`, since you'd
basically change everything to a box.  Otherwise I think expand/parse
is your only option.

[1] https://github.com/plt/racket/blob/master/pkgs/typed-racket-pkgs/typed-racket-lib/typed-racket/utils/mutated-vars.rkt

On Wed, Nov 20, 2013 at 12:35 PM, Emina Torlak <emina at eecs.berkeley.edu> wrote:
> I believe Robby's solution will work.  It's basically similar to what I had
> in mind, but I was hoping I'd be able to avoid it :)
>
> The crux of the issue is the need for unique identifiers for variable
> locations.  I also track updates to vectors and structs, for example, and
> this is easy precisely because I can use their identities to compute the
> "location" of the update.  So, boxing values in "raw" variables will have
> the same effect.
>
> In terms of implementation, though, it'll be trickier.  As far as I can
> tell, this approach involves whole-module analysis and transformation:
>
> * Expand the module completely.
> * Find all uses of set! and collect their targets.
> * Rewrite the def and all uses of target variables to perform auto boxing
> and unboxing.
>
> If there are tricks/constructs that I should use for the above steps, I'd
> love to hear them.   Or, of course, if there is some way to obtain variable
> location identifiers from the runtime, that would be great to know as well.
>
> Emina
>
>
>
>
>
> On Wed, Nov 20, 2013 at 5:19 AM, Robby Findler <robby at eecs.northwestern.edu>
> wrote:
>>
>> I didn't try it, but you it might work to use local-expand and then find
>> all the binding forms and then rewrite them to use boxes (or some other
>> source of uniqueness you might have around).
>>
>> Robby
>>
>>
>> On Wed, Nov 20, 2013 at 12:53 AM, Emina Torlak <emina at eecs.berkeley.edu>
>> wrote:
>>>
>>> This is how my solution currently works, but unfortunately, it's not
>>> quite right.  Here is a small example that demonstrates why, assuming the
>>> implementation based on free-id-table:
>>>
>>> (define (cell init)
>>>   (let ([x init])
>>>     (case-lambda
>>>       [() x]
>>>       [(v) (set! x v)])))
>>>
>>> (define foo (cell 0))
>>> (define bar (cell 1))
>>>
>>> > (foo 2)
>>> > (bar 3)
>>> > (foo)
>>> 2
>>> > (bar)
>>> 3
>>> > (dict->list global)
>>> '((.#<syntax:18:17 x> . 3))
>>>
>>> In the above scenario, I need the global map to contain two bindings:
>>> one for the location of 'foo.x' and the other for the location of 'bar.x.'
>>>
>>> Emina
>>>
>>>
>>>
>>> On Tue, Nov 19, 2013 at 10:31 PM, Sam Tobin-Hochstadt
>>> <samth at cs.indiana.edu> wrote:
>>>>
>>>> I think that just identifiers and `free-id-table`s should work here.
>>>> Here's your example:
>>>>
>>>> #lang racket
>>>>
>>>> (require syntax/id-table (only-in racket [set! #%set!]))
>>>>
>>>> (define global (make-free-id-table))
>>>>
>>>> (define-syntax-rule (location-of id) #'id)
>>>>
>>>> (define-syntax-rule (set! id expr)
>>>>   (let ([v expr])
>>>>     (dict-set! global (location-of id) v)
>>>>     (#%set! id v)))
>>>>
>>>> > (define x 1)
>>>> > (set! x 2)
>>>> > (set! x 3)
>>>> > (for/list ([(k v) (in-dict global)]) (list k v))
>>>> '((.#<syntax:4:8 x> 3))
>>>>
>>>> Also at https://gist.github.com/samth/7558673
>>>>
>>>> Sam
>>>>
>>>> On Mon, Nov 18, 2013 at 2:43 PM, Emina Torlak <emina at eecs.berkeley.edu>
>>>> wrote:
>>>> > I'm using Racket to implement a language for which I need to track
>>>> > state
>>>> > updates---in particular, variable mutation using set!.  For example,
>>>> > consider this module definition:
>>>> >
>>>> > #lang racket
>>>> >
>>>> > (require (only-in racket [set! #%set!]))
>>>> >
>>>> > (define global (make-hash))
>>>> >
>>>> > (define-syntax-rule (location-of id)
>>>> >   (#%variable-reference id)) ; doesn't quite do the right thing
>>>> >
>>>> > (define-syntax-rule (set! id expr)
>>>> >   (let ([val expr])
>>>> >     (hash-set! global (location-of id) val)
>>>> >     (#%set! id val)))
>>>> >
>>>> > When I evaluate the following sequence of forms against the above
>>>> > definition, I would like the global hash map to contain just one
>>>> > binding
>>>> > that maps the location for 'x' to the value 2.  With the above
>>>> > implementation I get two map entries, since variable-reference doesn't
>>>> > quite
>>>> > do what I hoped it did:
>>>> >
>>>> >> (define x 0)
>>>> >> (set! x 1)
>>>> >> (set! x 2)
>>>> >> x
>>>> > 2
>>>> >> global
>>>> > '#hash((#<variable-reference> . 1) (#<variable-reference> . 2))
>>>> >
>>>> > Is there another construct in Racket that I could use for this
>>>> > purpose?  If
>>>> > not, can something like this be implemented and how much work would it
>>>> > entail?
>>>> >
>>>> > I have a purely macro-based solution that works for the most part, but
>>>> > it's
>>>> > fragile and there are corner cases for which it is just wrong.  So,
>>>> > before
>>>> > trying to fix that, I was wondering if there is a nicer way to solve
>>>> > it by
>>>> > somehow getting handles for variable locations that are comparable
>>>> > using eq?
>>>> > or equal?
>>>> >
>>>> > Thanks!
>>>> >
>>>> > Emina
>>>> >
>>>> >
>>>> > ____________________
>>>> >   Racket Users list:
>>>> >   http://lists.racket-lang.org/users
>>>> >
>>>
>>>
>>>
>>> ____________________
>>>   Racket Users list:
>>>   http://lists.racket-lang.org/users
>>>
>>
>

Posted on the users mailing list.