Arc Forumnew | comments | leaders | submitlogin
Bind a list of variables
3 points by shader 5681 days ago | 13 comments
Hey all, just wondering how you would solve this:

I have a list of variables that I want bound to successive calls to readb. How would you go about doing this? I.e.:

  (each v '(a b c) (= ,v (readb in)))
Unfortunately, you can't unquote a variable that isn't already quasiquoted. I tried experimenting with a few macros, but it's late and I couldn't get anything to work. Suggestions?


4 points by rntz 5681 days ago | link

    (let (a b c) (n-of 3 (readb in))
       ...)

-----

1 point by Adlai 5681 days ago | link

I do feel stupid. However, this creates new bindings. From the code that shader posted, I think he doesn't want to create a new lexical binding. Is there some way of using destructuring for assignment?

-----

2 points by pg 5680 days ago | link

I don't understand exactly what you're looking for. Can you give a sample macro call, and what you'd like it to expand into?

-----

2 points by shader 5680 days ago | link

Both Adlai's and rntz's version do what I was trying to do.

However, I was looking for a way to assign a value to the variable referenced by the one I was passed. i.e.

  (= v 'b)
  (= (val v) 6) ; unknown function/macro val which returns contents of v so that '= can assign 6 to 'b.
This would allow the expression above to be:

  (each v '(a b c) (= (val v) (readb))) ;assigns three bytes from standard in to 'a, 'b, and 'c respectively.
Whether this is done by overloading 'unquote to work like that outside of a quasiquote, or making something called 'val, I don't know. Maybe it could be done by making val or unquote a setform? That would solve this problem, but I think it might be more useful if it worked in more places than just assignment.

-----

2 points by CatDancer 5680 days ago | link

If b is a global variable, it's easy.

If b is a lexical variable, eval won't help, since it isn't run in the lexical scope in which it's called. An Arc lexical variable will get compiled to an MzScheme lexical variable, and I don't know of a way to refer to an MzScheme lexical variable by name determined at run time. If there isn't, I suspect your only hope might be to modify the Arc compiler to generate code like

  (case v
    ((a) (set! a n))
    ((b) (set! b n))
    ((c) (set! c n))
    ((d) (set! d n))
    ...
for all the lexical variables available at that point.

-----

1 point by Adlai 5681 days ago | link

Alright, here is my first ever Arc code:

  (mac read-into (vars form)
    `(do
       ,@(map (fn (var) `(= ,var ,form)) vars)))
Note that this generates code which evaluates 'form once per variable, which I think is what you want.

For some reason, I couldn't for the life of me get the [_] syntax to work. Anyways, to do what you were trying, you'd just call, with my macro defined:

  (read-into (a b c) (readb in)))
By the way, I think that "readb in" can't be right. Maybe you want (readb (stdin))?

Adlai

EDIT:

Got it to work with the [] fn abbreviation, and it's the most hideous butchering of a beautiful thing that I've ever seen:

  (mac read-into (vars form)
    `(do
       ,@(map [quasiquote (= ,_ ,form)] vars)))
The problem is that

  [`(= ,_ ,form)]             ;expands into

  (fn (_) (`(= ,_ ,form)))    ;instead of

  (fn (_)  `(= ,_ ,form))     ;ugghh!!!

-----

1 point by absz 5681 days ago | link

The behaviour of the [] syntax makes perfect sense, if you think about it. First, [...] is just an abbreviation for (fn (_) ...) to save typing.[1] Second, `(...) is just an abbreviation for (quasiquote '(...)). Thus [`(...)] is [(quasiquote ...)], which is (fn (_) ((quasiquote ...)). If [] stripped off the outer layer of parentheses, then [+ _ 1] would become (fn (_) + _ 1), which is clearly wrong. Thus, we see that it makes more sense to use fn, since the whole point of [] and ` is to save typing and make code clearer, neither of which they can do here.

Another, possibly nicer, way to think about it is that [] just represents a delayed function call; you're trying to delay an object, and since [] is a function call, you're calling it instead. Either way, the point is that this is the consistent and desirable way for [] to work.

[1]: On Anarki, you also get _1, _2, etc., and __ for varargs, but that's not important for this discussion.

-----

1 point by shader 5680 days ago | link

Yep. Unfortunately, in this case it's practically shorter, and more readable, to use the fn form:

  (fn (_) `(= ,_ ,form))
because you don't have to type out the whole name "quasiquote".

-----

1 point by Adlai 5680 days ago | link

Just a little curious idea:

What about a special syntax (for this one situation), where you can put the backquote thus: [` = ,_ ,form] which would read-macro-expand to (fn (_) `(= ,_ ,form))

I guess it's just a question of how common this problem is, and how difficult it would be to implement this type of read-macro. I guess brackets.scm goes on my list of files to check out...

-----

1 point by shader 5680 days ago | link

in is just a input-port that I would already have bound, or passed in. If you'll notice, that was just a standalone expression, with no context, to illustrate what I was trying to do.

Thanks for the code, that was exactly what I was thinking of. Too bad there isn't a macro or function that just returns the value of a variable in such a way that it can be assigned to. Can anyone think of how to implement such a thing?

i.e.:

  (= v 'a)
  (= (val v) 6) ;the variable a is now assigned the value 6.
Basically an (unquote) without the wrapping quote ;)

-----

1 point by Adlai 5680 days ago | link

Well, there's always 'eval, but it has issues and stigmas, and I've read that other Lispers look at you funny when you use it your code...

'in is also a built-in macro in arc.arc. Since Arc is a Lisp-1 (grr...), you can't bind it to an input stream without potentially breaking some other code...

  (mac in (x . choices)
    (w/uniq g
      `(let ,g ,x
         (or ,@(map1 (fn (c) `(is ,g ,c)) choices)))))
Back to the symbol dereferencing question -- I think one reason that this kind of thing is shied away from is that it starts to smell an awful lot like pointers. However, the control over evaluating that you get with macros is more than enough to take care of symbol dereferencing problems like this.

-----

1 point by shader 5680 days ago | link

Right. I forgot to check the symbol before I used it like I sometimes do, via 'help or 'src (Anarki).

Is there anything wrong with "pointers"? As in this case, they can often be quite useful, and make the code (at least to me) simpler. Maybe I just think in pointers, and need to learn to use destructuring binding more. That won't work unless you know all of the variable names in advance, but I guess that's what a hash table is for :)

-----

2 points by almkglor 5679 days ago | link

Here's a simple Arc-F package (which is intended to work on hl in the future) that provides pointers.

Pointers are dereferenced by (ptr), so you use (ptr) to get the contents and (= (ptr) v) to assign.

  (in-package pointer)
  (using <arc>v3) ; replace with <hl>v1
  (interface v1
    pointer)
  ; replace with (mac (pointer e) ...)
  (mac pointer (e)
    (if (acons e)
        (let vars (map [uniq] e)
          `(with ,(mappend (fn (var val) `(,var ,val)) vars e)
             ; replace with (tag...)
             (annotate 'pointer
               (cons (fn () ,vars)
                     (fn (v) (= ,vars v))))))
        (w/uniq v
          `(annotate 'pointer
             (cons (fn () ,e)
                   (fn (,v) (= ,e ,v)))))))
  ; replace with (defm (sref (t p pointer)... ) ...)
  (defm sref ((t p pointer) v)
    ((cdr:rep p) v))
  (defcall pointer (p)
    ((car:rep p)))
Usage:

  (= stuff (list (pointer a)
                 (pointer (car b))
                 (pointer c!x)))
  (each p stuff
    (pr (p))
    (= (p) (something)))
If someone's ported nex3's 'defcall and 'defm onto Anarki-on-arc3, the above can be made to work by just removing the package stuff.

-----