Arc Forumnew | comments | leaders | submitlogin
4 points by waterhouse 1335 days ago | link | parent

I think it is really, really bad to have to type "(do x)" instead of "x" just to avoid macroexpansion. Additionally, I think it is unnecessary: you'd only have to do that because arc3.1 happens not to check for lexical variables when determining whether to macroexpand. This is different from the way it works in Scheme and Common Lisp, and I don't think it was a deliberate design decision, and it is really easy to change:

    (define (ac-call fn args env)
      (let ((macfn (ac-macro? fn)))
  -     (cond (macfn
  +     (cond ((and macfn (not (lex? fn env)))
               (ac-mac-call macfn args env))
  --ac.scm
So just treat it as a bug in arc3.1--it probably won't even cause problems most of the time, because it's kind of a rare case--and fix it in your own ac.scm, and assume that it will be fixed in future users' Arc implementations. Please do not establish "(do x)" as good coding style. If the compiler screwed up whenever you had a variable that had three vowels in a row, the solution would be to fix the damn compiler, not to awkwardly twist up your own code; and if other people were using the old broken compiler, you'd first tell them to upgrade, and only change your own program to cater to the bad compiler if it was absolutely necessary for some reason--and you'd do so after you'd gotten your program the way you wanted it.

(See also: http://arclanguage.org/item?id=13581 and http://arclanguage.org/item?id=11685. I learned this from the latter thread.)

Edit: It is probably a hard problem to diagnose if you just leave it there and someone uses it and is like "why is this screwing up?". So if you intend for others to use it, you could put in a thing like this:

  (mac achtung (x) `(+ ,x 2))
  (let achtung [+ _ 5]
    (unless (is (achtung 0) 5)
      (err "Oh god you have a bad Arc compiler.  Fix that crap:
            http://arclanguage.org/item?id=13606")))


3 points by rocketnia 1335 days ago | link

> So just treat it as a bug in arc3.1--it probably won't even cause problems most of the time, because it's kind of a rare case--and fix it in your own ac.scm, and assume that it will be fixed in future users' Arc implementations.

I'm all too happy to do that. ^_^ Note that it'll probably mean I've introduced bugs to Rainbow and Jarc. :-p

Part of the reason I've harped on the state of affairs that causes me to write (do x), as well as the reason the state of affairs isn't altogether bad (that it makes macros that generate macro forms a tiny bit less susceptible to variable capture), has been in the hope that people will become confident that it's really annoying. ^^;

I honestly didn't know I'd be the only one actively defending it (rather than merely leaving things the way they are or upvoting), so I continued to give it the benefit of the doubt. Let's not do that. :)

> it probably won't even cause problems most of the time, because it's kind of a rare case

Actually, I use local variable names that clash with macros all the time, and that's why I started my (do x) and (call x) patterns in the first place. :) If I remove the cruft right away, my code will almost certainly break until everyone has upgraded. Dunno if anyone but me is going to suffer from my (published) code breaking, but hey. :-p

---

By the way, in this case, I was programming to a different core language implementation--in fact hacking on something core to that implementation--and I admit I cargo culted.

-----

1 point by rocketnia 1335 days ago | link

Here's a patch for ar: https://github.com/rocketnia/ar/commit/3e2a1396c898caaf2dfe6... ^_^

It oughta make locals shadow macros, but again, it's untested.

-----

2 points by aw 1335 days ago | link

Thanks! I incorporated your patch and waterhouse's test.

(I ended up moving the check for lexical variables into the extension to ac-call to simplify macex).

However, this raises an interesting issue.

setforms calls macex which calls ac-macro?.

So do we still have the same bug with set expressions?

That is, even with this patch, can we construct a set expression that incorrectly treats a lexical variable reference as a macro?

-----

1 point by rocketnia 1334 days ago | link

I was hoping to actually try running some code last night, but no dice. Anyway, that sounds like it would in fact be a problem. In fact, in Arc 3.1 at least, 'defset things are looked up at compile time, and 'get is given special treatment. Try something like this:

  (= foo (table) bar (table) baz (table) qux (table)
     quux (list nil nil))
  (mac get1 (x) `(,x 1))
  (let ref bar (= (ref foo) 2))
  (let get idfn (= ((get baz) qux) 3))
  (let cadr car (= (cadr quux) 4))
I wouldn't worry about 'get too much. It's inconsistent in just the same way as metafns are, so it's just one more in a list of names we shouldn't overwrite or use for local variables.

The setforms case is a bit more troublesome. Maybe they shouldn't be macro-like, and should instead be special-case extentions of 'sref? If the return value of 'get were its own type with 'defset and 'defcall support, that case could be eliminated too.

-----

2 points by akkartik 1335 days ago | link

This may be a case where I've painted myself into a corner with wart. Since wart is built atop a lisp-2, I can't just expand the ssyntax a.b to (a b). I have to generate (call a b). Since I want to be able to say things like ++.a, I need call to handle macros as well. But now (call f x) will try to call a macro called f before it looks for the lexical binding.

It's not as big a problem as it may seem. You don't have to worry about future macros shadowing lexical bindings as long as they load afterwards.

The biggest compromise I've had to make because of this: using call-fn (which doesn't expand macros) in accumulate (https://github.com/akkartik/wart/blob/ed9a7d4da1fa017188fce2...) because I wanted to name one of the keyword args unless. So you seem to be watching over your creation after it's left the nest :)

(tangent)

I spent an embarrassingly long time trying to have lexical bindings override macros, before realizing that's impossible in wart: macros get expanded long before lexical bindings are created. So this is a case where you really need a full interpreter; any macro you write can't inspect lexical bindings up the call stack. (oh, for python's nested namespaces..)

(even bigger tangent)

Wart update: arc.arc is just about done. I'm going to start on the webserver, probably not srv.arc but palsecam's http.arc (http://www.arclanguage.org/item?id=11337).

I ended up dividing up ac.scm into 17 files, and arc.arc into 26 (the boundary is fuzzy). So far each file seems highly coherent, and most files are short, so the codebase feels the way the old-timers described forth code: "A component can usually be written in one or two screens of Forth." (http://prdownloads.sourceforge.net/thinking-forth/thinking-f..., pg 41; screens are Forth's units of code.)

-----

3 points by rocketnia 1335 days ago | link

But now (call f x) will try to call a macro called f before it looks for the lexical binding.

Hmm... I'm totally guessing, but this might do the trick:

  -(defmacro call(f &rest args)
  +(defmacro call(f &rest args &environment env)
     (cond
  -    ((macp f)   `(,f ,@args))
  +    ((and (symbolp f) (macro-function f env))   `(,f ,@args))
       ((compose-form-p f)   (expand-composition f args))
       (t  `(call-fn (fslot ,f) ,@args))))
If I'm not mistaken, this looks up f in the lexical environment now, so it should also allow 'call to work with lexically scoped macros.

More information about &environment: http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec...

-----

1 point by akkartik 1335 days ago | link

Great idea, thanks!!

But it doesn't seem to quite work:

  $ sbcl
  * (macro-function 'unless)
  #<FUNCTION (MACRO-FUNCTION UNLESS) {1000ADDAB9}>
  * (macro-function 'foo)
  NIL
  * (let ((unless 34)) (macro-function 'unless))
  #<FUNCTION (MACRO-FUNCTION UNLESS) {1000ADDAB9}>
  * (defmacro macp(x &environment env) (macro-function x env))
  MACP
  * (let ((unless 34)) (macp unless))
  #<FUNCTION (MACRO-FUNCTION UNLESS) {1000ADDAB9}>
  * (let ((unless 34)) (macp foo))
  NIL
Hmm, it's possible it'll only work for dynamically generated macros.. Or am I doing something wrong?

-----

1 point by rocketnia 1335 days ago | link

Remember, you're in a lisp-2. Try this:

  (macrolet ((unless() nil)) (macp unless))  ; should be truthy
  (flet ((unless() nil)) (macp unless))      ; should be falsy
Come to think of it, I don't remember what problem we were trying to solve here. XD Functional position won't use regular local variables anyway....

-----

1 point by akkartik 1335 days ago | link

Lol.

I have a macro called call. Given (call f arg), I would like to return:

if f is a local lambda, (call-fn f arg)

if f is a macro, (f arg)

otherwise, (call-fn f arg)

So yes, it probably isn't possible after all..

-----

1 point by rocketnia 1334 days ago | link

Hmm. Maybe you could shoot for two ssyntaxes, so that it's possible to explicitly override whatever the default rules would do:

  f.arg -> (call f arg)
  f@arg -> (f arg)

-----

1 point by akkartik 1334 days ago | link

Yeah, that may be a good option.

-----

1 point by akkartik 1334 days ago | link

http://blog.jayfields.com/2011/02/clojure-and.html

-----

1 point by evanrmurphy 1335 days ago | link

> I ended up dividing up ac.scm into 17 files, and arc.arc into 26 (the boundary is fuzzy). So far each file seems highly coherent, and most files are short

Ok, I started reading wart. :)

Skimmed that Forth reading. I've only dabbled in Factor, but I'm sometimes tempted to explore concatenative programming more in depth.

-----

1 point by akkartik 1335 days ago | link

Thanks!

Next time somebody says lisp has too many parens I'm going to tell him to put his money where his mouth is and learn forth :)

-----

1 point by akkartik 1334 days ago | link

http://37signals.com/svn/posts/2760-rpn-ever-4-

-----