Arc Forumnew | comments | leaders | submitlogin
1 point by evanrmurphy 5130 days ago | link | parent

> why would I ever want the language to do this for me

Hmm... so would you also prefer that return statements never be implicit?

I'd like to design the bottom layer operators of this compiler to have a one-to-one correspondence with JavaScript and zero automagic. This means explicit var declarations, explicit returns and an embrace of the expression/statement dichotomy. Then, we should be able to layer on top all desired forms of automagic using macros so that programmers can opt-in or opt-out as they like.

Or is it really naive to think it could work out this way? :P



2 points by rocketnia 5130 days ago | link

Nah, that comment was only about function scope. I'm saying I really prefer the kind of scoping Arc and JavaScript both have, where variables shadow each other and closed-over variables don't need to be declared (but local ones do). I can't put my finger on why... but wait, pg can. :D

http://www.paulgraham.com/arclessons.html

Skip to "Implicit local variables conflict with macros."

As for implicit return statements, I don't know what you expect me to be, but I'm for them. ^_^ I think you can always say "return undefined;" in JavaScript to accomplish the same thing as leaving out the return statement, so in a way, leaving the return statement out is just syntactic sugar.

I'm for explicit return statements too, for the purposes of short-circuiting. However, they probably won't play well with macros, for the same reason as pg encountered with implicit local variables: Lexical scope boundaries might pop up in unexpected places and change the meaning of "return;".

> Or is it really naive to think it could work out this way? :P

Well, I do have lots of suggestions. They go pretty deep into the fundamental design too. That said, they sort of lead to not programming in Arc. You'll see what I mean in a second (unless I go scatterbrained on ya again).

I think it would be best to treat this as a combinator library. You can have utilities that build not only JavaScript expressions and statements,* but also other intermediate forms, like lists of statements. I wouldn't be surprised to see http://en.wikipedia.org/wiki/Control_flow_graph pop up at some point. As the combinator library goes on, it can introduce more convenient construction syntaxes, including full DSLs.

If this is beginning to sound similar to the topic I just posted, "A Language Should Target Moving Platforms"[1], that's because it is. We're talking about generating JavaScript code. It's very closely related to the topic of generating code in general. :-p

I was developing Penknife not too long ago. It's a language much like Arc, but what's important here is that its expressions are compiled and macro-expanded to an intermediate format first--i.e. parsed into an AST--and it's easy to make closures whose ASTs can be inspected. My plan is for the Penknife AST itself to be extensible using user-defined types, and my goal is for non-Penknife ASTs to be made using the same syntax technology used elsewhere in Penknife, right down to using the same compiler.

So yeah, this is my apprach to a JavaScript DSL. ^_^ I don't expect you to go adopt the whole approach, but if you do want to pursue it, or if there's some aspect of it that would fit into your design too, be my guest.

* Fine-grained control is fine by me. :-p So is automagic. I want it all!

[1] http://arclanguage.org/item?id=12939

-----

1 point by akkartik 5130 days ago | link

"Lexical scope boundaries might pop up in unexpected places and change the meaning of "return;"."

I couldn't believe this, but I looked around and yes, it's true, "..the scope container in javascript is a function." (http://mark-story.com/posts/view/picking-up-javascript-closu...)

That seems bad. Don't create a new function in js everytime I do a let in arc. But I don't know if you can avoid doing so.

Perhaps one way to do it would be to play a little fast and loose with semantics. Assume let translates to the scope of the containing function.

-----

1 point by evanrmurphy 5130 days ago | link

> I couldn't believe this, but I looked around and yes, it's true, "..the scope container in javascript is a function."

Yep! Function scope + no tail-call optimization = a formidable challenge. ^_^

> Perhaps one way to do it would be to play a little fast and loose with semantics. Assume let translates to the scope of the containing function.

Interesting approach. I'll play around with this. There might also be weird hacks that could simulate let without costing a whole function on the stack. For example, you might be able to get away with something like this:

  (let x 5         var _temp = true;
    ... )          while (_temp) {
                     _temp = false;
                     var x = 5;
                     ...
                   }
Or this:

  (let x 5         var _temp = true;
    ... )          for (var x = 5; _temp = false; _temp)                           
                     ...
                   }
But I'm not sure yet if these work, or if they're less expensive than the straightforward let translation:

  (let x 5         (function() {
    ... )            var x = 5;
                     ...
                   }).call(this);

-----

2 points by rocketnia 5130 days ago | link

I tried these out, and they don't seem to help, at least in Chrome:

  var x = 1;
  {
      var x = 2;
      var y = "but why!?";
      alert( x );           // 2
  }
  alert( x );               // 2
  alert( y );               // but why!?
  
  var x = 1;
  do
  {
      var x = 2;
      var y = "but why!?";
      alert( x );           // 2
  }
  while( false );
  alert( x );               // 2
  alert( y );               // but why!?
The next thing you might try is mangling variable names. If you expect the JavaScript code to never use threads (which it doesn't, I think), you could temporarily replace the variable values using assignment.

For a high-level (and non-idiomatic) approach, it's possible to implement trampolining using exceptions. I've done it in about a page of JavaScript (right next to my Fermat's Last Theorem proof, of course of course :-p ), and I think the API can be as simple as this:

  - Use bounce( foo, foo.bar, 1, 2 ,3 ) instead of
      foo.bar( 1, 2, 3 ) in tail positions. This throws an
      exception containing information about the next call
      to make.
  - To begin a trampolining loop, call the function with
      trampoline( foo, foo.bar, 1, 2, 3 ). This should be
      done when calling functions at the top level and when
      calling functions from within functions that are to be
      exposed to other JavaScript utilities and DOM event
      handlers (which won't call trampoline themselves).
Okay, so determining when a closure could "escape" into the outside world might be hard. O_o To avoid that trouble, uh, keep a global variable saying that a trampolining loop is already in progress, and use trampoline( ... ) for almost every single call? Well, that'll complicate things a little. ^_^;

-----

1 point by evanrmurphy 5130 days ago | link

> The next thing you might try is mangling variable names.

I think this could work pretty nicely, actually:

  (let x 5         var _x;
    ... )          _x = 5;
                   ...
Here's an example with a global and local variable:

  (= x 6)            var x, _x;
  (let x 5           x = 6;
    (alert x))       
  (alert x)          _x = 5;
                     alert(_x); // => 5

                     alert(x); // => 6
If you use multiple lets with the same variable name in one scope, you would need to differentiate with additional underscores or something:

  (let x 5           var _x, __x;
    (alert x))         
  (let x 6           _x = 5;
    (alert x))       alert(_x); // => 5

                     __x = 6;
                     alert(__x); // => 6
Of course, there is still some risk of variable name collision, and the output isn't super readable. Maybe it's good to have two operators, `let` and `let!`. The former uses function scope, so it's safer and compiles to more readable JavaScript, but it increases the stack size. The latter (let!) is the optimized version that uses one of the schemes we've been talking about.

-----

1 point by rocketnia 5129 days ago | link

What's your policy regarding the ability for people to say (let _x 5 ...) to spoof the mangler? ;) Also, will the name "let!" conflict with ssyntax?

These questions shouldn't keep anyone up nights, but I thought I'd point them out.

-----

1 point by evanrmurphy 5129 days ago | link

> What's your policy regarding the ability for people to say (let _x 5 ...) to spoof the mangler? ;)

The underscore prefix system would be nice because it's pretty readable, but it's not necessary. In the worst case, we can use gensyms. I thought using underscores for internal variables might work because CoffeeScript does it [1], but maybe it won't. So you raise a good point. :)

> Also, will the name "let!" conflict with ssyntax?

Yes. I'm starting from scratch with the ssyntax. `!` is a regular character now. It can be used in names like `let!`, and when by itself in functional position it compiles to the JavaScript negation operator:

  (! x)   =>   !x
`.`, `:`, `~`, `[]`, `&` and friends are out, too. This may displease my Arc buddies, but I just found it too difficult to think in JavaScript when the gap in meaning between Arc and JS for these characters was so large. `.` compiles to the JS dot operator now, `{}` to the JS object literal and `[]` to the array literal (see [2] for some details). Note that quote ('), quasiquote (`), unquote (,), unquote-splicing (,@) and string quoting (") should all work as expected.

---

[1] http://jashkenas.github.com/coffee-script/#comprehensions

[2] http://arclanguage.org/item?id=12948

-----

1 point by akkartik 5130 days ago | link

Probably just different aesthetics. I sense that you're thinking in javascript and rocketnia is thinking in arc.

-----