I'm not following the two of you on precisely what this anti-feature is. Assigning to local variables using '=' does not create new global variables. Can you share a code sample showing what you're talking about?
>So you could have lib#1 set var 'x' only for it to get clobbered by lib#2's version of var 'x' and arc just ignores this. That's bad. Really bad.
And how often is it intended behavior? How often does one want to have a macro parameter or apparently "local" variable overwrite an existing symbol globally and arbitrarily?
I can see it being useful in the case of "hot patching" code to just redefine a function, but that's still kind of crazy, since now the source code and running code are no longer the same. Might as well just edit the file and reload it, and have one source of truth.
It seems like an anti-feature. Something that seemed like a good idea in theory, but which in practice does more harm than good.
This definitely not great, but having to use 'with' is not the real issue for me. In fact if you read through the code you'll notice that this style is often used deliberately (i.e. some people might argue that it's a feature).
I think the bigger issue is that global vars can be used without any errors or conflict warnings. So someone else can create code which sets globals which then gets loaded via libs and will pollute your program. So you could have lib#1 set var 'x' only for it to get clobbered by lib#2's version of var 'x' and arc just ignores this. That's bad. Really bad.
Yeah, as a newcomer I can attest that I did not know that. V here being global is really counter-intuitive:
(defun f ()
(= v 10)
I would have expected (and would prefer) the default to be to bind to whatever the current scope is, and to have global (file level, then application level) scope be opt-in rather than opt-out. We can't assume that it's more likely new users will be familiar with lisp idioms.
It looks like the particular solution you'd like is for `=` to act as it does in Python, but I think Arc's behavior is preferable to Python's.
For a variable to "default" to "global scope" is basically the definition of how lexical scope works. To find a variable's binding occurrence, you look outward until you find it. Once you go far enough out, you get to the language definition itself, which ultimately must provide some "global" catchall case.
Python makes lexical scope much more complex to describe: Every variable is declared at a module (or REPL), class, or function boundary. To find the boundary where a variable is declared, first you look for the nearest boundary. If you've found a module boundary or a function boundary where the variable is in the parameter list, you're done. Otherwise, you search that boundary for any `=`, `global`, or `nonlocal` declarations of that variable. If you find `global`, you skip to the nearest module boundary, and you're done. Otherwise, if you find one or more `=` declarations and don't find `nonlocal`, you're done. Otherwise, you repeat this process at the next nearest boundary.
I do sympathize with a couple of the pain points you're talking about:
1. If a variable name is written with a typo, Arc's `=` will silently assign to a misspelled global binding. I think it'd be more ideal to alert the programmer to an error like C does.
2. To declare a local variable, an Arc programmer must use things like `let` and `with`, which add layers of parentheses and indentation. In C (ever since C99) and Python, local variable declarations can be performed in the middle of a block, and they don't affect the indentation of the following code.
For point 1, most of the time I like to program without using `=` at all.
For point 2, I think Lisp's prefix notation and lambda notation facilitate a style that uses more deeply nested expressions than you might be used to. When expressions can be more deeply nested, they don't have to be broken apart across quite as many local variables, so writing `let` and `with` forms here and there doesn't pose a serious problem most of the time. That said, I use what I call "weak opening parens" and a certain indentation style to do away with this extra nesting anyway (https://github.com/lathe/parendown-for-racket).
I see. Yes, it can be a little sharp-edged for new-comers from other languages that `=` auto-defines globals. It's not immediately obvious that we don't use it much. Let me think about how to improve that.
I have been thinking about a modified version of LET that takes a sequence of expressions (like begin) that are either assignments or not assignments. Then it would collect all consecutive assignments together into a letrec. For example:
(= a 1)
(= b 2)
(= c 3)
(= x 4)
(= y 5)
He wants it to be a little more like scheme wherein:
(define v 10)
creates v as a local variable. In Arc, and most lisps, you need to do:
(defun f ()
(let ((v 10))
Additionally, Arc has the wrinkle (i think, i haven't used Arc recently) where if you do:
(defun f ()
(= v 10)
you create a global variable and assign 10 to it, instead of the python default which creates v in local scope. Basically, he's trying to use imperative programming and doesn't want to declare local variables using let.
Sorry I just saw this. Can you elaborate? What is an example of a variable that C and Python define correctly but has a worse equivalent in Arc? Or what's a variable in Arc that bit you by being globally scoped?
In particular I'm surprised that you mentioned C. If we're worse than C in this respect I'd love to understand that better.
I will say; I did a page reload and noticed the color pick was still in place and had to go hunt for the x. I expected it to reset, but I'm not sure if you see that as a bug or a feature :)
also, it looks like the client side is pure js... Is Lumen not up to that task? just curious if you tried that as it might be nice if, someday, we could get to the point where this kind of stuff could be done in arc.
> I was aware that Arc kills threads that take longer than 30 seconds...
> Sooo yeah, submitting changes via JS is easy. Getting updates is the hard part.
That's why I suggested a post request...
Just deliver the page and create an api end point, on the server, that gives you the last x changes from the change log.
With that ajax request, you could return these net updates for the client and use client side js to apply them to the dom. You could even create a trivial js debounce algo to throttle requests if you wanted to go even further.
We started out by making an EventSource endpoint and using JS EventSource object to get updates. Seemed to work great.
Pushed it live.
To everyone on the site. Not just to the people on /l/place. Oops. Mistake #1.
Mistake #2: I was aware that Arc kills threads that take longer than 30 seconds, but I was unaware that EventSource automatically tries to reconnect. Welcome to the DoS party.
With no way to tell the clients to stop. Mistake #3.
It was only live for about 10 minutes, but it took around two hours before the last few clients finally refreshed their browser and stopped DoSing the endpoint. The server absorbed the extra traffic like a champ, but the server logs were ridiculous for about an hour. Lots of "client took too long" error messages spit out by Arc every second.
Sooo yeah, submitting changes via JS is easy. Getting updates is the hard part.
Could poll. But then that's still hammering the server. Moreso than it already is.
It's probably a feature that it's painful to use. Only the determined will use it for art.
That said, if anyone has ideas for how to tweak arc for performance for this scenario – and remember to calculate how much memory is used by how many simultaneous sockets are open – I'd be willing to try again.
>This is where js would be handy - right? Personally I would just update the dom element and send a http post in the background.
If it's to be done in JS, might as well implement a drawing tool in an actual canvas element.
I've seen tutorials that are reasonably simple. The complicated part would be actually saving it. It is possible to export a canvas as a png and upload it with an AJAX request but I don't know how cross-browser compatible that is.