Arc Forumnew | comments | leaders | submitlogin
Quick question - un-setting a symbol?
2 points by lacker 5905 days ago | 14 comments
Is there any way to undo a set operation?

More specifically, I want to have macros tempset and cleartemps. tempset works just like set, but when cleartemps is called, all the variables that were set with tempset get unbound.

Maybe I'm not thinking of this in the right way... if there's a way to get this behavior with let/with and ccc or something like that, please enlighten me. I would also be amenable to not having this ugly global state, and instead of the cleartemps behavior just having some macro that when execution leaves the macro block, all the internally tempset variables get destroyed.

Thanks!



3 points by eds 5905 days ago | link

Maybe I missed the point of your post, but if you just want temporary variables inside a block of code, can't you just use let?

  (let var val
    ; use var
  )
  ; now var is gone
Or use with if you want to declare mutliple variables.

This works if what you want is temporary local variables. (And it's better than tempset because it uses the stack to restore previous values of variables, and can create nested scopes.)

If on the other hand you are talking about creation of temporary global variables, then you might need something a little fancier.

Or perhaps you are talking about something like unintern from CL? I admit it might be kind of nice to have explicit intern and unintern primitives. But that is more of a namespace issue than merely an unsetting a variable.

-----

4 points by lacker 5905 days ago | link

Yeah, like unintern from CL. Although more closely something like python's del.

in python:

  >>> a = 3
  >>> a
  3
  >>> del a
  >>> a
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  NameError: name 'a' is not defined

edit: I investigated a little more and found mzscheme's namespace-undefine-variable! I think this is going to work. Thanks for the hint ;-)

-----

5 points by almkglor 5905 days ago | link

I suggest the use of closures rather than actually using global variables - global state without the global namespace clutter ^^

-----

2 points by lacker 5905 days ago | link

Yeah, definitely. But, this specific problem is in the context of writing a restricted eval, like for a browser plugin or something like that. I was thinking of doing a global replace of set with tempset and then cleaning up after one plugin runs. But maybe that is the wrong way to do it.

-----

4 points by drcode 5904 days ago | link

It might make more sense to have the eval prepend a suffix on all variables before "evaling"- Sort of like a poor man's namespace.

-----

2 points by eds 5904 days ago | link

I still don't see why something like this wouldn't work for most cases:

http://arclanguage.org/item?id=4481

If you did this for all local variables, and then used namespace-undefine-variable! to delete any defs that had been evaluated, I think you would get a fairly well restricted eval.

-----

4 points by lacker 5904 days ago | link

I agree with you - I think this is roughly the right idea, I just hadn't found namespace-undefine-variable! when I asked this question.

I think there's still somewhat of a problem with wrapping variables in a "let" with their deep copies - that prevents internal code from wrapping builtins and having other builtins use the wrap, because the let will make the builtin-wrap scope different from the outer scope. But you can do the special-suffix thing in this case.

-----

1 point by absz 5905 days ago | link

So the idea would be that you would write

  arc> (tempset x 10)
  10
  arc> (+ x 10)
  20
  arc> (tempset y -10)
  -10
  arc> (+ x y)
  0
  arc> (cleartemps)
  nil
  arc> (- x y)
  Error: "reference to undefined identifier: _x"
as opposed to

  arc> (let x 10
         (prn:+ x 10)
         (let y -10
           (+ x y)))
  20
  0
  arc> (- x y)
  Error: "reference to undefined identifier: _x"
? If so, I'm not sure how to do it--I don't see a good way. However, there should be some way to write a macro w/temps such that

  (w/temps
    (tempset x 10)
    #;(...))
does what you want; it would be (at a minimum) tricky, though.

-----

2 points by lacker 5905 days ago | link

The part I can't figure out is how to make

  (w/temps
    (def foo (x) (+ x y))
    (tempset y 10)
    (foo 5))
work correctly.

-----

1 point by aston 5905 days ago | link

Might wanna have w/temps work more like

  (w/temps x y
    (def foo (x) (+ x y))
    (tempset y 10)
    (foo 5))
That is, like a with, but with x and y set to some dummy values. You wouldn't even need a tempset then, right?

-----

2 points by bogomipz 5904 days ago | link

Right indeed. The normal way to do this would be;

  (with (y nil foo nil)
    (= foo (fn (x) (+ x y)))
    (= y 10)
    (foo 5))
You can't use 'def there because, unlike in Scheme, def always makes a global binding in Arc. Including x in the with is not necessary, by the way.

From the sound of it, this does not solve lacker's problem, however, because he does not know up front what variables he needs to declare.

-----

1 point by absz 5904 days ago | link

As a first cut, I would scan through to find the tempsets, take out the variables, and then expand to a let or with block now that you know their names.

-----

2 points by almkglor 5904 days ago | link

  (mac make-things-difficult (c v)
    (if (some-property v)
      `(tempset ,c ,v)
      `(if (another-property ,v)
          (tempset ,c ,v)
          (= ,c ,v))))

-----

1 point by absz 5904 days ago | link

Ah. Right. (And I even worried about similar things when writing make-br-fn...). Well, it would work in simple cases, but let/with are looking like better ideas.

Actually, if we add another primitive (with $ or xdef) which works using mzscheme's namespace-undefine-value!, we could have tempset maintain a global list of tempset variables, and cleartemps go through and undefine them.

-----