Arc Forumnew | comments | leaders | submitlogin
Why does (= x 12) expand to ((fn nil (assign x 12)))?
4 points by jazzdev 5358 days ago | 6 comments
Is there any reason why (= x 12) doesn't just expand to (assign x 12)? I can't see any advantage in wrapping a thunk around it. Seems like it's just adding overhead. But maybe that gets compiled away.


3 points by rocketnia 5358 days ago | link

The = macro and a bunch of others use 'do, which is what ultimately does the thunk-wrapping. (You can see this from the REPL if you use 'macex1 rather than 'macex.) So if the thunks bother you, redefine 'do:

  (mac do lines
    (if cdr.lines
      `((fn () ,@lines))
      car.lines))
I can't guarantee this won't mess something up, but I did a search of arc.arc and couldn't find anything that would break from this, and I suspect most Arc code is the same way. I abuse 'do a bit in my own code, using (do.foo a b c) to avoid accidentally macro-expanding foo, and even that works just as well either way.

The fact that Arc didn't already give 'do a special case like this surprised me at first too, but at least it makes arc.arc about a dozen tokens shorter. :-p

-----

2 points by jazzdev 5358 days ago | link

Thanks. You guys are great. I get the reasoning and a work-around! It's not that it bothers me. I'm working on a compiler (for Jarc) and wanted to make sure it's okay to optimize away the thunk.

-----

2 points by rocketnia 5357 days ago | link

If Jarc still has the macro semantics I mentioned at http://arclanguage.org/item?id=11621, it may not be okay. That's because usually a call to a thunk suppresses the expansion of expressions inside it:

  Jarc>
    (mac bar ()
      (do (prn "expanding")
          4))
  #3(tagged mac #<procedure>)
  Jarc>
    (def foo ()
      (bar)
      (prn 1)
      (and (bar))  ; expands through '(bar) to 4
      (prn 2)
      (idfn (bar))
      (prn 3)
      (do (bar)))  ; expands to '((fn nil (bar)))
  expanding
  expanding
  #<procedure>
  Jarc> (foo)
  1
  2
  expanding
  3
  expanding
  4
  Jarc> (foo)
  1
  2
  expanding
  3
  expanding
  4
  Jarc>
As you can see, (bar) in a function body is expanded to 4 as the function is defined, even if it's the result of expanding (and (bar)), but (do (bar)) is only expanded to ((fn () (bar))), and expansion stops here because it's a function application. In regular Arc, 'ac compiles function applications by compiling all their subexpressions, but apparently this doesn't happen in Jarc.

Like I touched upon in the other thread, if you were to "fix" this, that would break some of Jarc's ability to use lexically bound macros. Specifically, it would make it impossible to shadow a global macro with a local binding ('cause it would be compiled too soon). Lexically bound macros would still be available to an extent, since another two of Jarc's differences are that 'eval uses the lexical environment and that macro applications which somehow miss being compiled, like (let x bar (x)), are called with their arguments unevaluated (like fexprs).

On the other other hand, this fexpr ability kinda begs for function applications not to be compiled in all cases, or else what the fexpr sees will probably be the compiled arguments rather than the original ones.

Anyway, hope this helps you figure things out. ^_^

-----

1 point by jazzdev 5357 days ago | link

Having just read http://arclanguage.org/item?id=11621 I see now how badly Jarc's macro expansion is broken. I'll fix this ASAP.

Jarc wasn't designed to allow lexically bound macros, that is just an accident of the current (broken!) implementation.

Jarc originally had dynamic binding, which is why eval uses the lexical environment. I should have changed that when I ripped out the dynamic binding. I'll fix eval. I'd like Jarc to be consistent with arc3.1

Thanks! Definitely helps my figuring.

-----

1 point by jazzdev 5354 days ago | link

Macro expansion timing has been fixed in Jarc 10, released today.

-----

4 points by evanrmurphy 5358 days ago | link

It's needed for multiple variable assignments. As you've noted, for a single variable assignment like (= x 12), (assign x 12) is as good as ((fn () (assign x 12))). But for a multiple variable assignment like (= x 12 y 34), whose expansion is ((fn () (assign x 12) (assign y 34))), taking out the thunk causes an error.

-----