How to modify the scope chain without `with` ?
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 discouragewith
and it (iirc) is ignored in strict mode - and you can't use thevm
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 abovetemplate
function. Again - I am NOT talking about howthis
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") };
For what it's worth very popular templating libraries like KnockoutJS use woth
heavily.
I think the consensus is that writing DSLs should be done as a transformation into JavaScript (like JSX) and not inside JavaScript (like Knockout and your library)
The dynamic nature of with
is why it is forbidden in strict mode, when import/export land in browsers things will run in strict mode by default which means with
is gone.
This npm module might help you out. www.npmjs.com/package/with
It is used by several engines for similar effects without the strain on the VM "with" causes.
On Mon, Feb 15, 2016 at 4:48 AM, Benjamin Gruenbaum <inglor at gmail.com> wrote:
For what it's worth very popular templating libraries like KnockoutJS use
woth
heavily.I think the consensus is that writing DSLs should be done as a transformation into JavaScript (like JSX) and not inside JavaScript (like Knockout and your library)
The dynamic nature of
with
is why it is forbidden in strict mode, when import/export land in browsers things will run in strict mode by default which meanswith
is gone.
Aside from this, I wish JS would provide the ability to modify the scope chain - and it should be just objects that can inherit from one another through prototypes. I think it would be too perfect/clean to handle scope in the existing way we handle inheritance. My example isn't completely justified to say "I need this" - but I think having direct control over scope would be important in the future and should be considered for ES7. Especially if the existing way is not portable anymore...
I don't think it would be a security concern either, as you could only make objects you have reference to your new scope.
On Mon, Feb 15, 2016 at 1:43 AM, Andreas Rossberg <rossberg at google.com> wrote:
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 forwith
, otherwise it will just check the target object, which has nof
.(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
Thank you very, very much! I feel embarrassed I missed that it needed a has(), but I am glad I have a solution that works now - I thought it impossible!
On 15 February 2016 at 18:49, Coroutines <coroutines at gmail.com> wrote:
On Mon, Feb 15, 2016 at 4:48 AM, Benjamin Gruenbaum <inglor at gmail.com> wrote:
For what it's worth very popular templating libraries like KnockoutJS use
woth
heavily.I think the consensus is that writing DSLs should be done as a transformation into JavaScript (like JSX) and not inside JavaScript (like Knockout and your library)
The dynamic nature of
with
is why it is forbidden in strict mode, when import/export land in browsers things will run in strict mode by default which meanswith
is gone.Aside from this, I wish JS would provide the ability to modify the scope chain - and it should be just objects that can inherit from one another through prototypes. I think it would be too perfect/clean to handle scope in the existing way we handle inheritance. My example isn't completely justified to say "I need this" - but I think having direct control over scope would be important in the future and should be considered for ES7. Especially if the existing way is not portable anymore...
That would be a total and complete disaster for implementations, as it
would make scopes observable and mutable in a way that absolutely breaks
all conventional compilation and optimisation techniques for local
variables, closures, etc. JavaScript would easily become 10-100x slower.
Like when you use with
today.
I don't think it would be a security concern either, as you could only
make objects you have reference to your new scope.
It would totally break security as well, given that functions could then peek into local variables on their call chain through proto. And I cannot even fathom what it would mean to mutable some proto!
On Mon, Feb 15, 2016 at 11:51 PM, Andreas Rossberg <rossberg at google.com> wrote:
That would be a total and complete disaster for implementations, as it would make scopes observable and mutable in a way that absolutely breaks all conventional compilation and optimisation techniques for local variables, closures, etc. JavaScript would easily become 10-100x slower. Like when you use
with
today.
It was not my plan to move closed-over values and local variables into an Object as part of a chain of prototyped, inherited objects. I only wanted to make it possible to set the "global" environment, and have that object follow prototypical inheritance.
To this I would ask - which specific optimisations would it compromise?
It would totally break security as well, given that functions could then peek into local variables on their call chain through proto. And I cannot even fathom what it would mean to mutable some proto!
In what I envisioned, it would not expose local or closed-over variables - they would not be part of the environment Object I'm talking about.
My inspiration is that you could prepare sandbox environments that you
construct and derive from cleanly - without using something like the
vm
module only found in Node. Currently if you want to "sandbox"
the environment in the browser you have to lose references to things
you consider dangerous (of course) - but that is "forever". You
cannot leave any reference to anything unsafe and once it's done it's
done. Having the ability to derive from "global" (only in Node) and
prepare an Object to run an function within as its global context
would be an invaluable ability. (imo)
2016-02-16 15:51 GMT+01:00 Coroutines <coroutines at gmail.com>:
Having the ability to derive from "global" (only in Node) and prepare an Object to run an function within as its global context would be an invaluable ability. (imo)
It seems like an obvious idea, but in fact it's almost impossible to secure
- consider
true.constructor.constructor("alert('XSS')")()
ECMAScript lacks secure sandbox that would work in every browser, but such limited scope manipulation is totally useless as "secure sandbox".
BTW, such limited scope manipulation is already possible, see how my
library works there -
Ginden/reflect-helpers/blob/master/tests/closures.js#L14
(it heavily uses eval
).
Sending again because of wrong "to".
On Tue, Feb 16, 2016 at 7:45 AM, Michał Wadas <michalwadas at gmail.com> wrote:
2016-02-16 15:51 GMT+01:00 Coroutines <coroutines at gmail.com>:
Having the ability to derive from "global" (only in Node) and prepare an Object to run an function within as its global context would be an invaluable ability. (imo)
It seems like an obvious idea, but in fact it's almost impossible to secure
- consider
true.constructor.constructor("alert('XSS')")()
ECMAScript lacks secure sandbox that would work in every browser, but such limited scope manipulation is totally useless as "secure sandbox".BTW, such limited scope manipulation is already possible, see how my library works there - Ginden/reflect-helpers/blob/master/tests/closures.js#L14 (it heavily uses
eval
).Sending again because of wrong "to".
Okay - different argument: if you can provide actual environment inheritance you can avoid collisions assigning to the "global scope".
But then, you might as well start using modules and properly scoping variables, as that will lend to much more readable code.
(Sent again, wrong "from")
/#!/JoePea
Seems like changing the global could lead to problems where a developer might assumes certain globals but hey different ones, which imho could make the use of globals even messier. I imagine you would like Angular's $scope inheritance, which is sort of like what you want ( angular/angular.js/wiki/Understanding-Scopes).
/#!/JoePea
/#!/JoePea On Feb 16, 2016 9:13 AM, "/#!/JoePea" <joe at trusktr.io> wrote:
Seems like changing the global could lead to problems where a developer
might assumes certain globals but hey
I meant: "but have" different ones. Importing things explicitly into modules (instead of (inherited) globals) helps better understand the requirements of a given piece of code imo.
different ones, which imho could make the use of globals even messier. I
imagine you would like Angular's $scope inheritance, which is sort of like what you want ( angular/angular.js/wiki/Understanding-Scopes).
/#!/JoePea
On Feb 16, 2016 9:10 AM, "/#!/JoePea" <joe at trusktr.io> wrote:
But then, you might as well start using modules and properly scoping
variables, as that will lend to much more readable code.
(Sent again, wrong "from")
/#!/JoePea
On Feb 16, 2016 7:57 AM, "Coroutines" <coroutines at gmail.com> wrote:
On Tue, Feb 16, 2016 at 7:45 AM, Michał Wadas <michalwadas at gmail.com>
wrote:
2016-02-16 15:51 GMT+01:00 Coroutines <coroutines at gmail.com>:
Having the ability to derive from "global" (only in Node) and prepare an Object to run an function within as its global context would be an invaluable ability. (imo)
It seems like an obvious idea, but in fact it's almost impossible to
secure
- consider
true.constructor.constructor("alert('XSS')")()
ECMAScript lacks secure sandbox that would work in every browser, but
such
limited scope manipulation is totally useless as "secure sandbox".
BTW, such limited scope manipulation is already possible, see how my
library
works there -
On Tue, Feb 16, 2016 at 9:16 AM, /#!/JoePea <joe at trusktr.io> wrote:
/#!/JoePea On Feb 16, 2016 9:13 AM, "/#!/JoePea" <joe at trusktr.io> wrote:
Seems like changing the global could lead to problems where a developer might assumes certain globals but hey
I meant: "but have" different ones. Importing things explicitly into modules (instead of (inherited) globals) helps better understand the requirements of a given piece of code imo.
different ones, which imho could make the use of globals even messier. I imagine you would like Angular's $scope inheritance, which is sort of like what you want (angular/angular.js/wiki/Understanding-Scopes).
I was just thinking about the import
we gained in ES6. In Perl and
Python you might have functions imported directly into the global
scope (of the current context) - so that it's easier to just write the
function name without the short reference you imported it in as:
cos(PI) vs Math.cos(Math.PI) etc...
I was simply thinking it could give you the ability to choose how you prefer to write that. If you're not using several modules and the code you're writing is quite focused in one area it might look cleaner (less redundant) to import Math and have all its functions be accessible as "globals". If you are writing code that ties together functions from Math, fs, and tls modules (for some reason??) it would make sense to do the existing:
var fs = require('fs'); var tls = require('tls'); ...
Anyway, if we had control over the global scope/context/Object, it would allow you to make these design decisions in an effort to write code that I would sometimes view as cleaner. What I think is important is that you could have your existing scope inherit from the "first" global scope, but when you assign to the current global scope it is set there. Your modifications don't extend globally to the rest of the project. Also if you want to isolate your current scope from the global scope you don't have to have one inherit from the other - you can break it's prototype (or never form it). Again, as the other guy pointed out - it's not a complete or even marginally safe "sandbox" but all in all I think it's something we should have control over if we want to simplfy how modules are created (for one) without polluting the existing global scope/Object.
My responses are a bit wordy/disorganized...
On Mon, Feb 15, 2016 at 1:13 AM, 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.
I think you'll have a lot better luck starting out like I did: Learn HTML first. You're working on some complicated stuff there, and for me, I always try to get simple to work. Simple and working is good!
You cannot have too solid of a grasp of HTML. Ditto for CSS.
When it comes to javascript, context and scope can be tricky and confusing for those coming from other languages.
Thank you,
/#!/JoePea On Feb 16, 2016 9:38 AM, "Coroutines" <coroutines at gmail.com> wrote:
On Tue, Feb 16, 2016 at 9:16 AM, /#!/JoePea <joe at trusktr.io> wrote:
/#!/JoePea On Feb 16, 2016 9:13 AM, "/#!/JoePea" <joe at trusktr.io> wrote:
Seems like changing the global could lead to problems where a developer might assumes certain globals but hey
I meant: "but have" different ones. Importing things explicitly into
modules
(instead of (inherited) globals) helps better understand the
requirements of
a given piece of code imo.
different ones, which imho could make the use of globals even messier.
I
imagine you would like Angular's $scope inheritance, which is sort of
like
what you want (angular/angular.js/wiki/Understanding-Scopes).
I was just thinking about the
import
we gained in ES6. In Perl and Python you might have functions imported directly into the global scope (of the current context) - so that it's easier to just write the function name without the short reference you imported it in as:cos(PI) vs Math.cos(Math.PI) etc...
You could
let {cos, PI} = Math
console.log(cos(PI))
On Tue, Feb 16, 2016 at 9:38 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:
On Mon, Feb 15, 2016 at 1:13 AM, 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.I think you'll have a lot better luck starting out like I did: Learn HTML first. You're working on some complicated stuff there, and for me, I always try to get simple to work. Simple and working is good!
You cannot have too solid of a grasp of HTML. Ditto for CSS.
When it comes to javascript, context and scope can be tricky and confusing for those coming from other languages.
Thank you,
With respect - for this particular project I wanted to gut it to be as small as possible. What fascinates me about this module (ck) is how it abuses lookups in the scope chain to form HTML - while similarly looking great as valid Coffeescript. It has been my aim to remove as many responsibilities from it to instead put on the user or on other libraries. I do not want this module to worry about if <lol> is a
valid HTML tag - just that the tag itself is well-formed (disregarding self-closing tags). I have already removed auto-indenting the resulting HTML as that can be done with js-beautify in another part of the overall pipeline (a gulp task for instance).
The frustration I'm having is that with
is no longer in style. I
can get it to work with a Proxy (as Andreas corrected for me) - but it
still requires I keep a list of valid HTML tags - so the has() handler
in the proxy will work correctly. The problem is the scope lookup I
need is in the wrong order - I wanted it to look at the existing scope
and if a function doesn't exist, then resolve it through the Proxy so
it creates the closure which creates the HTML tag. Currently this is
reversed - which is why I have to keep the list of HTML tags. If I
could reverse it I could remove more code.
I don't have the needed control over scope I want - I can't resolve through the existing scope and fallback on a Proxy to generate a function/closure as needed. That is my problem with scope.
Anyway, I understand if this is not the right place to talk about my specific project problem - but I thought it would be important to start a discussion about scope. I'm lobbying to have this control someday. Again, with WebAssembly in the future to support the scoping rules of other languages that will compile to the WebAssembly AST it will have to be loosely based on what Javascript supports.
Here's the non-working version of what I had in mind (forgive the coffeescript) - but it somewhat illustrates what I'm talking about with "with + Proxy" (dynamic?) scoping:
require('assert').ok Proxy?, 'no Proxy!!' require 'harmony-reflect'
html = ''
isString = (x) -> typeof x is 'string' or x instanceof String
env = new Proxy {}, # this is a problem - I can't make use of existing functions # "behind" the Proxy if I always return true (from the existing, non-with scope) has: -> true get: (_, prop) -> (attrs, body) -> makeTag prop, attrs, body
makeTag = (tag, attrs, body) -> unless typeof attrs is 'object' body = attrs attrs = {}
attrs = ("#{attr}=\"#{value}\"" for own attr, value of attrs
when value? and value isnt false).join ' '
# intended coercion here
if attrs
attrs = " #{attrs}"
html += "<#{tag}#{attrs}>"
# if there is no body, this is a self-closing tag (the user
knows HTML not us) return unless body?
if isString
html += body
else
runWithinTagger body
html += "</#{tag}>"
f() creates an <f> - this is flawed. in the ck module a Function is
created from the edited toString() of f:
blitmap/ck/blob/master/lib/ck.coffee#L82-L84
function runWithinTagger(f) { with (env) { f(); } }
f = -> h1 'Hello', 'this header here' p 'this is a paragraph'
runWithinTagger f
console.log html
On Tue, Feb 16, 2016 at 9:57 AM, /#!/JoePea <joe at trusktr.io> wrote:
You could
let {cos, PI} = Math console.log(cos(PI))
Oh I agree there - if you're only using a few functions from a module it makes sense to create local references to them. I just thought something like this would be nice if you are drawing upon 20+ functions from Math and you don't want to write "Math." everywhere. Even "m.".
tmp = Object.assign({}, Math);
tmp = Object.setPrototypeOf(tmp, _ENV);
_ENV = tmp; # "global" scope chain would be: Math -> previous module
scope object
exports.someFunction = (x) => cos(PI);
...
...
...
To me "don't use with
- assign the object to a smaller reference"
just seems unsustainable.
Say you're using a bunch of functions from 'ReallyLongModuleName'.
So you do something like this:
var x = require('ReallyLongModuleName');
And you start doing this everywhere:
x.something(); x.thatThingOverThere(); console.log(x.Whatever() + x.thisIsAnnoying());
It just becomes a chore to write "x." everywhere - which is what makes
with (x) { ... }
so attractive.
Again, this is predicated on you wanting to use say 15+ functions from a module that has a really long name. And that's assuming it's the only module with a really long name. Some people (me) have trouble coming up with short, meaningful names for things which ordinarily would have long names. So these people (me) would use common short identifiers - like: var $ = require(...);
Well shit, now we're confusing people further. var x, y, z, $ = require('this has to end somewhere');
Global scope should be something you can replace and prototype. Maybe not for security - not a sandbox - but for managing how concisely one can refer to the functions they need to. (Again: For instances where you're using many functions from a module and it makes sense to just populate the object acting as the global scope with them).
We should be able to manage global
/window
better, and I think this
has merit for ES7.
PS: with
only allows you to put an object at the front of the scope
chain, what I'm suggesting would allow you to fallback on another
scope with inheritance.
PPS: Some modules overwrite the identifiers they use in the global scope, even when they assign into module.exports (which is naughty). Being able to portably construct the global scope object a function runs in can prevent this messiness. We need Node's nodejs.org/api/vm.html#vm_vm_runincontext_code_contextifiedsandbox_options
Personally I'd prefer a special keyword:
_ENV = {}; // empty global scope, no Array, no String, but still reachable through [].prototype of course.
FYI, this part might not work as you might expect. I've already been tripped up by similar.
var o = {
index: 0,
func() { return this.index++ },
}
with (o) {
func() // TypeError: Cannot read property 'index' of undefined
}
The other reasons with
is deprecated are because dynamic scoping is
extremely hard to optimize and it's no longer obvious what variables
are defined in that block (static analysis no longer works).
As for the global scope, I'll refer you to the System.global proposal: tc39/proposal-global Isiah Meadows me at isiahmeadows.com
On Mon, Feb 22, 2016 at 4:07 AM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
FYI, this part might not work as you might expect. I've already been tripped up by similar.
var o = { index: 0, func() { return this.index++ }, } with (o) { func() // TypeError: Cannot read property 'index' of undefined }
To me this is expected... I am accustomed to 'use strict'.
The other reasons
with
is deprecated are because dynamic scoping is extremely hard to optimize and it's no longer obvious what variables are defined in that block (static analysis no longer works).
All I'm proposing is that we be given the ability to replace the global scope/Object by assigning to a special reference (_ENV = {}) - which would be scoped to the block, not forever-changed throughout the entire project/runtime. It shouldn't change the layout of how the global scope is referenced on the stack in any implementation (v8? Chakra?). And then of course that global scope object could be prototyped like any other object to provide inheritance/additional scoping.
I am used to having this control in Lua - it works really nicely
there. I hate to be "that guy" but I've seen this used to help people
manage and keep their code readable. It's all just managing
namespaces. If I'm writing a module that defines a lot of Math
functions I want the scope I define these functions in to be imbued
with the existing facilities from Math
. I want to directly invoke
cos() not Math.cos(). From the point of view of my math-related code
I don't want to have the main, global perspective of the script that
is loading my module. I might make use of other modules that do not
have a Math focus, and for that I will associate identifiers that go
with their function: let log = require('logging');
To me this makes perfect sense, but the only way to do this (assuming
we never use with
again) is for every module to be referenced
through a local, possibly shortened identifier and not touch the
global scope at all. Repeating identifiers over and over is not
concision, it's not DRY - it's tedium. I cannot be as expressive as I
want, nor can I make use of the environment I need - and mess it up
(temporarily). On that 2nd note, I wish module loading were as simple
as this to prevent global destruction:
Loading a module should be:
-
Resolve to the path of the module (or <script> href?)
-
Fetch/read the file/script
-
Construct a global env/object that inherits from the existing environment
-
Run the script as a function/block within this new environment
-
Cache the
exports
value. -
Return this value to the caller
I think Node does this essentially (which is why we have a global
reference to the "real" environment), but "normal" JS does not. We
hide what we can in closures, but code that does not export in the
many fabulous ways we've come up with can have a lasting effect on the
global scope. I like that to explicitly mutilate the global scope
(not module-scope) I have to type global[prop] = ...
.
Personal opinion: I prefer runtime analysis to static analysis. Not a huge fan of type annotations and "strong mode" trying to cement a type's ABI to eek out more performance. I mean yes, optimizations are great but I'd rather have flexiblity to express myself how I want over limitations that save me negligible amounts of time..
Okay. Now I understand what you're wanting. That's something that I'd expect would be expensive, but I'm not fundamentally against it. I'm into performance as well (and not just "I'm into it because it sounds bad if I'm not"), but performance is a weird beast at times. I've found that occasionally, the more dynamic features also help tremendously. Also, strict typing is mostly useful if you're writing something at scale. I tend to lean to dynamic features in smaller things, and static style in larger libraries and applications. I see benefits to both.
Back to the original topic, I feel swapping the global is potentially dangerous, but you should hopefully know what you're doing if you do.
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 discouragewith
and it (iirc) is ignored in strict mode - and you can't use thevm
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 abovetemplate
function. Again - I am NOT talking about howthis
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() };