> at least your approach will generalize consistently to the case (= x y z w).
Yes, that's the reason. (= x y) was a poor example since you don't actually need the wrapping function for single assignment. In fact, I've provided a way to do it without:
arc> (js `(assign x y))
> That way this has the same value inside and outside the block.
Very astute! ^_^ I had become aware of the problem of this changing values but didn't know how to fix it. I will try your 'call approach soon. Thanks a lot!
arc> (ssexpandall ''dont-expand-me.im-quoted)
I think the way you're going to go about it, by having (quote ...) forms be compiled, has a bit of a caveat. If you're already planning to have a!b expand to (a 'b) and compile to "a.b", then won't (js '(eval 'foo)) just result in "eval.foo"?
All that being said, I bet you already support eval(), in a way:
what would be (js '(eval '(alert (eval '(+ 1 2)))))
is expressed as (js `(eval ,(js `(alert (eval ,(js '(+ 1 2)))))))
The difference here is just syntax sugar, IMO. (Saving parentheses is a fine goal of syntax sugar, though!)
Maybe string concatenation ... is in fact a good counterpart to quasiquotation and I should compile it...
That feature would be a bit more difficult to simulate if 'js didn't support it intrinsically. Here's a quick approach:
(mac jswith (bindings . body)
`(fn-jswith (list ,@(map .1 bindings))
(fn (,(map .0 bindings)) ,@body)))
(def fn-jswith (vals body)
; We're adding the suffix "v" to each name so that it isn't the
; prefix of any other name, as might happen with gs1234 and gs12345,
; with this suffix.
(withs (strnames (map [string (uniq) 'v] vals)
names (map sym strnames))
`( (fn ,names
(eval ,(multisubst (map [list (+ "('+" _ "+')") _)] strnames)
(mac jslet (var val . body)
`(jswith (,var ,val) ,@body))
now what would be (js '(eval `(+ 1 ,foo)))
is expressed as (js `(eval ,(jslet f 'foo
(js `(+ 1 ,f)))))
where the final form sent to 'js is
(eval ((fn (gs1001v) (eval "'(1+('+gs1001v+'))'")) foo))
or expressed as (js:jslet f 'foo
(js `(eval ,(js `(+ 1 ,f)))))
where the final form sent to 'js is
((fn (gs1001v) (eval "'eval(\'(1+('+gs1001v+'))\')'")) foo)
I do feel that this difference is more than sugar, since the 'foo subexpression is moved out of context.
Additionally, there's an elegance to reserving unquote for escaping Arc code, which would be difficult to do if you compiled quasiquotation.
Well, the Arc quasiquotes are processed before 'js even sees the input, right? Here's the only problem I see (and maybe it's exactly what you're talking about):
A typical way to escape from two levels of nested Arc quasiquotes is ",',", as in `(a `(b ,c ,',d)). That constructs something that includes a (unquote (quote ...)) form, so it only works when you're sending the result somewhere where unquote-quotes don't matter (like, to be evaluated as Arc). So ideally, the 'js meanings of 'quasiquote and 'quote should have this property. I don't think this would be especially hard to guarantee, but it might be easy to miss.
(Note that if Arc's quasiquotes didn't nest, the same example would be expressed as `(a `(b ,',c ,d)), and no unquote-quote would hang around to be a problem. I'm beginning to wonder if nesting quasiquotes are a wart of Arc.)
arc> (ssexpand 'a:.b)
(compose a .b)
arc> (ssexpand '.b)
So, we need to recurse in the f argument anyway. At a certain point, it seems like the anonymous & higher-order functions add layers of indirection on what should just be a straightforward recursive definition.
I get really annoyed at that, though, when working with trees in Arc. There always seems to be some underlying pattern that's just different enough that I can't abstract it into a higher-order function.