How to modify the scope chain without `with` ?

Andreas Rossberg rossberg at google.com
Mon Feb 15 09:43:58 UTC 2016


On 15 February 2016 at 10:13, Coroutines <coroutines at gmail.com> wrote:

> This post might be overly wordy.  Sorry.  It relates to the
> functionality provided by the `with` keyword and why I think it's
> important in the future.
>
> I am currently rewriting a templating module that I think is very
> useful for it's ability to turn a function in coffeescript syntax into
> a sort of DSL - something that looks like this:
>
> template = ->
>   doctype 5
>   html ->
>     head ->
>       title @title
>     body ->
>       div id: 'content', ->
>         if @posts?
>           for p in @posts
>             div class: 'post', ->
>               p p.name
>               div p.comment
>       form method: 'post', ->
>         ul ->
>           li -> input name: 'name'
>           li -> textarea name: 'comment'
>           li -> input type: 'submit'
>
> For those not familiar with Coffeescript, "= ->" creates a function
> with no arguments, the indented sub-block is the body of the function.
>
> All of these things essentially compile into nested functions like:
> html(head(title(this.title))
>
> (not an exact translation)
>
> Anyway, this library/module called (ck) exploits the little-used
> `with` keyword.  It creates a function like this:
>
> function (scope, template) { with (scope) { template(); } }
>
> So the template is just a series of functions that lookup within
> `scope` for a function creating HTML.  The problem is this module
> (imo) wastefully creates a lot of closures to create the HTML tags.
>
> It was my plan to create a Proxy object to use like: with (proxy) {
> ... } - so html() called within that `with` block would redirect
> through the proxy to something like: makeTag('html', children...)
>
> This does not work.  Proxies as objects provided to `with` does not
> work.  I don't know if this is intended but I'm disappointed.  `with`
> itself is a keyword discouraged from use (it seems).
>
> I am from Lua, where in Lua we have 2 variables called _ENV and _G.
> In Javascript terms _G would point to `global` in node (the main
> execution context/object).  _ENV has no direct mapping to JS - it
> would be the current context/object, which might not be _G anymore.
>
> I wish it were possible to create objects that functions could run
> within - you can seemingly only do this with the outmoded `with` or
> with the 'vm' module in Node.  People seem to discourage `with` and it
> (iirc) is ignored in strict mode - and you can't use the `vm` module
> in the browser.
>
> I think there is a need for the ability to do this in ES7, and I wish
> it were as simple as assigning an object to _ENV to change the
> environment the function dereferences/resolves through.
>
> Am I crazy or is this a good idea?  The MDN posting on the `with`
> keyword says you should just create a short reference to make use of
> things - like: ck.p("this is a paragraph"); - but then this isn't as
> natural as exploiting the context of what the function is running in
> for the above `template` function.  Again - I am NOT talking about how
> `this` is defined but the outer scope/object.  I wish scope lookup
> were as simple as following a prototype chain.  I wish I could easily
> create a scope to run in from an object.
>
> Would this be something useful - or is `with` just not in style
> anymore?  (I'm still mad I can't use a Proxy in with):
>
>   require('harmony-reflect');
>
>   let f = function() {
>     cats('abc');
>     dogs('123');
>     thisshouldjustlog('damnit');
>   };
>
>   let tmp = new Proxy({}, {
>     get: function() {
>       return console.log;
>     }
>   });
>
>   // disappointment abound
>   with (tmp) { f() };


Without wanting to say much on the overall viability of your plan, but
proxies do work with `with`. However, your code has at least two bugs:

(1) It's not defining a custom `has` trap. That is needed for `with`,
otherwise it will just check the target object, which has no `f`.

(2) You cannot return `console.log` first-class and expect it to work (at
least not on all browsers). Known JavaScript issue.

This fixed version works fine on V8 4.9 / Chrome 49:

function f() { console.log("failure") }

let p = new Proxy({}, {
  has() { return true },
  get() { return x => console.log(x) }
});

with (p) { f("success") };

/Andreas
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160215/569b8333/attachment.html>


More information about the es-discuss mailing list