How to modify the scope chain without `with` ?

Coroutines coroutines at gmail.com
Mon Feb 15 09:13:03 UTC 2016


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() };


More information about the es-discuss mailing list