Yeah it lets you decide what to do when getting or setting a variable. The original link has an example at the bottom, but here's another one kinda related to what you seem to be trying to do:
arc> (= h (obj a 1 b 2))
#hash((a . 1) (b . 2))
arc> (defvar a
(fn args
(if args
; write
(= h!a car.args)
; read
h!a)))
arc> a
1
arc> (= a 3)
3
arc> a
3
arc> h
#hash((a . 3) (b . 2))
I read the link you suggested in my previous post about defvar, and I see that it can be used to set dynamic behavior when a variable is referenced (??). Is this the correct interpretation, and what might be some uses of that?
Wow great post, thanks for the reply. I didn't know eval was always executed at global scope, and I was not thinking about the ht variable being executed multiple times at different scope, so thanks for pointing that out. The rest of your explanation about how to get it working is very clear and I will be a better arc hacker for it!
The occasion for this macro is an object system built around closures and hash tables. I will post more about it when I've made more progress.
The issue was network (firewall) related. I simply
destroyed the firewall (it's on VM so am not concerned about security on this one). On ubuntu that amounted to removing the IPtable rules.
Am looking forward to putting this behind Nginx and seeing how that goes.
Thanks for sticking with me kinnard. It's very much appreciated. Have a great week.
The essential trouble you'll have with this macro is that Arc macros don't know what variables are in their caller's lexical environment.
(let a 1
(w/tab (obj b 2)
(+ a b)))
Since the place `b` will be looked up isn't determined unil run time, it has to use `eval`. But a call to `eval` always uses the global scope, so we lose `a`.
We could work around this by changing the way Arc's macroexpander worked. For now, let's not worry about that. I'll return to this later.
---
I see some particular bugs that can be fixed in your code.
First, you're inserting the bound values as flattened expressions in your generated code, so they'll actually be flattened executed. That is, if you write (w/tab (obj x '(+ (+ 2 y) 4)) ...), then I think `x` will be bound to the function `+`, `+` will be bound to 2, and `y` will be bound to 4.
If that's not what you want, we can make this change:
Second, you're assigning a nonlocal variable there, `res`. I don't know if you were doing that on purpose, but here's a version that avoids doing that:
Third, as you observed, the (eval ...) inside the (each ...) throws an error. What's going on is that `eval` evaluates things in the global scope, and you're trying to get a local variable. I recommend taking out the (each ...) loop and replacing it with a sequence of assignments. This means the keys you're assigning to will be determined based on the initial state of the table, rather than the final state, but that corresponds to the local variables that have been made anyway.
Fourth, this code evaluates the `ht` expression each and every time it does an assignment. This might be okay, except it's evaluating the expression in the local scope the first time and the global scope all the other times, which will probably lead to annoying errors. Let's evaluate it only once, in the local scope:
I'm doing something tricky there: I'm inserting the table itself as a quoted value. It's possible to avoid this with a pattern like ((eval `(fn (x) (... x ...))) x), but Arc lets us just do (eval `(... ',x ...)).
Er, actually, something's making that code not work on Anarki, even though it does work on Arc 3.1. Embedding a function (instead of a table) works on Anarki, so let's do that:
arc> (let a 1 (w/tab (obj b 2) (+ b b)))
4
arc> (let a (obj b 2) (w/tab a (= b 4)) a)
#hash((b . 4))
arc> (let a 1 (w/tab (obj b '(+ (+ 2 y) 3)) (join b b)))
(+ (+ 2 y) 3 + (+ 2 y) 3)
---
As I mentioned above, there's one case that's sadly impossible to support without hacking on the language a bit:
arc> (let a 1 (w/tab (obj b 2) (+ a b)))
Error: "reference to undefined identifier: _a"
Okay, here's a really hackish way to add this capability to the language.
We have effectively called `eval` in a local scope. We constructed the local scope table explicitly and then explicitly set it up again in the generated code.
---
If anyone would like to incorporate that six-line hack I demonstrated into Anarki, I think it will be a bit nicer if (get-latest-macex-env) were dynamically scoped rather than permanently mutated on each call. This will only ever matter if a macro invokes `eval`, `macex`, or `macex1` during its own macroexpansion, but hey, it could happen!
(This is effectively very similar to Kernel fexprs. The similarity would be even stronger if the environment were a table that mapped each variable to its local macroexpander. Arc doesn't have locally scoped macros, but I think this is a good way to go if we want them.)
I'm on the move at the moment, but since you brought up defvar yesterday I wonder if that would be a simpler way to go..
Also, it's not clear why you're using the top-level eval. I think it might be unnecessary. Double unquoting is hard to get right so worth avoiding if at all possible. Edit 2 hours later: oh, I see the reason for the eval.
I like it! I don't think it'll break anything; can you send a pull request? Then we'll be able to run such code reliably at the repl! That would be sweet.
Edit 38 minutes later: hmm, there's one issue. Right now you can type multiple expressions in on a single line, but this change would drop everything after the end of the first expression. A better approach would be to drop only whitespace and stop at the very first non-whitespace character.
I was using defvar where I should have been using =. But the idea in my post was just to store the result of (prompt) in the variable x, which is meant to be a string.
And yes, now that I am moving past the experimental phase of my script, I will be running things in batch mode.
I think you're misusing defvar. You can read about defvar at https://awwx.ws/defvar2. In short, to invoke your prompt function everytime you refer to x, say this:
arc> (defvar x (fn() (prompt "> ")))
For example:
arc> (len x)
> abc
3
BTW, I tend to avoid using the interactive repl when I'm performing raw I/O because of the need for the (readc) hack you mentioned. I'd rather just write my code in a script and run it in batch mode.
Surprised this story got no love. It's providing a powerful new medium/platform for lisping . . . I expect it to have a big impact on the size and character of the lisp community.
This is a stab in the dark but have you opened the port to receive connections from other "machines". Maybe the firewall is blocking incoming connections.
Also, it might help if you copy exactly your terminal input.
Yes, my impression is that "stack-based lisp" describes http://factorcode.org to a 't'. In Factor you can throw code on the stack by a process called.. quoting. And then invoke it inside some other higher-order function.