Using monocle mustache for chaining.
document.querySelector('#my-element').{ style.{ 'color': 'red', 'padding': '5px' }. textContent: 'Hello' };
I don't think I've seen this form of the .{ syntax discussed before. Is an expression being used as the LHS of an object literal property assignment? What does "foo.{ bar.baz: 12}" mean exactly?
Luke
For some reason, the code in the original most formatted with a lot of vertical white space in my mailer. I tried to fix it below: On Nov 11, 2011, at 10:50 AM, Erik Arvidsson wrote:
We've all looked at jQuery code and envied the conciseness of its chaining APIs. Most of us also looked at it and thought; Yuk, making everything a method of jQuery and always return the jQuery object is ugly.
However, I (with some help) realized that the .{ operator can be used to solve most of these chaining scenarios without any changes to any API.
Take this jQuery sample code:
$('#my-element'). css({ 'color': 'red', 'padding': '5px' }). text('Hello');
With the 'stache:
document.querySelector('#my-element').{ style.{ 'color': 'red', 'padding': '5px' }. // should be , instead of . ?????????????? textContent: 'Hello' };
One could easily see some extensions to make this even more useful. For example allowing method calls and member lookups etc would sweeten the deal. Here is a more advanced example that assumes we have some useful properties on the returned DOM collections:
jQuery:
$('#myTable'). find('.firstColumn'). css({'background': 'red', 'border': '2px solid black'}). text('first column'). end(). find('.lastColumn'). css('background','blue'). text('last column');
With the 'stache:
document.querySelector('#myTable').{ querySelectorAll('.firstColumn').{ style.{background: 'red', border: '2px solid black'}, textContent: 'first column' }, querySelectorAll('.lastColumn').{ style.{background: 'blue', border: '2px solid black'}, textContent: 'last column' } };
An extension that I've consider is to allow nested stashes:
foo.{ bar: 5, //add "bar" property to foo baz.{bam:6} //add "bam" property to object that is the value of foo.baz }
Using [ ] bracketed property keys, they could be computed:
foo.{ bar: 5, //add "bar" property to foo [foo.someSringValuedFunction()].{bam:6} //add "bam" property to object that is the value of foo.baz }
but it isn 't clear that it would cover what you are trying to do there. Also I no longer like [ ] for computed property names in literals...
I've also been told that this is just another form of Smalltalk's message cascade.
Maybe a cascade essentially captures the first method "receiver" and uses it for a sequence of method calls. I smalltalk:
foo bar; baz; bam.
(; is the cascade operator) pretty much means: temp = foo. temp bar. temp baz. temp bam.
On Fri, Nov 11, 2011 at 11:27, Luke Hoban <lukeh at microsoft.com> wrote:
document.querySelector('#my-element').{ style.{ 'color': 'red', 'padding': '5px' }. textContent: 'Hello' };
I don't think I've seen this form of the .{ syntax discussed before. Is an expression being used as the LHS of an object literal property assignment? What does "foo.{ bar.baz: 12}" mean exactly?
I haven't seen that form either ;-) Allen suggested allowing .{ on the LHS which would mean get the value of the property and then apply the properties of the RHS to that.
-- erik
I'm sorry, i don't understand the semantics you're going for here. What is document.querySelector('#myTable').{ querySelectorAll('.firstColumn').{ style.{background: 'red', border: '2px solid black'}, textContent: 'first column' }, querySelectorAll('.lastColumn').{ style.{background: 'blue', border: '2px solid black'}, textContent: 'last column' } };
meant to do?
the outermost .{ is of the form .{expression, expression}
Which isn't valid.
But ignoring that, the decision we made at last november's meeting was that DOM attributes would be specified as setters on the prototype, so
someElement.style.{background:"blue", border: "2px solid black"}
would add to own properties to the style instance, but not actually change the style properties in the CSSOM, as .{ shares the normal object literal semantic of only performing own property assignment.
It appears to be assigning multiple properties in place and at once and returning the original object if I am understanding correctly.
On Fri, Nov 11, 2011 at 11:38, Oliver Hunt <oliver at apple.com> wrote:
But ignoring that, the decision we made at last november's meeting was that DOM attributes would be specified as setters on the prototype, so
someElement.style.{background:"blue", border: "2px solid black"}
would add to own properties to the style instance, but not actually change the style properties in the CSSOM, as .{ shares the normal object literal semantic of only performing own property assignment.
Yeah, sorry for being hand wavey here. The intended behavior I envisioned would be [[Put]] and not define property. Maybe that is pushing the limits too far?
On Nov 11, 2011, at 10:50 AM, Erik Arvidsson wrote:
We've all looked at jQuery code and envied the conciseness of its chaining APIs. Most of us also looked at it and thought; Yuk, making everything a method of jQuery and always return the jQuery object is ugly.
Beauty is in the eye of the beholder... jQuery made combinator libraries cool, and for that this functional programmer is permanently indebted to jresig.
But really, there's a world of difference between the chaining done by libraries like jQuery and the chaining you get from this operator. The former is pure, the latter is a mutation. The .{ syntax obscures this fact, and I find that inexcusable.
I'm not saying mutation is bad -- JS is no pure functional language, nor should it be. But creating an assignment operator that doesn't suggest syntactically that it's an assignment, and worse, advertising it as a "chaining operator" as if it were the same thing as what jQuery does, makes the same mistake of masking mutation that so many imperative programming languages make. Languages should be up front when they use mutation and not try to hide it or confuse programmers into not knowing when they're doing it.
Just for contrast, here's what your example might look like with a more explicit assignment syntax:
document.querySelector('#my-element') .= {
style .= {
'color': 'red',
'padding': '5px'
},
textContent: 'Hello'
};
But that nested thingy smells awfully funny to me. This reminds me of excessive uses of "point-free style" in Haskell, where people do back-flips just to avoid creating variable names for intermediate results. Variables aren't evil! Sometimes it's just cleaner to use a local variable:
let elt = document.querySelector('#my-element');
elt.style .= {
'color': 'red',
'padding': '5px'
};
elt.textContent = 'Hello';
Worse, notice that you have provided only an ability to do deep mutation here, and no way to do a functional update (such as a copy with the specified changes, or a prototype extension with the changes) on nested structure. My spidey-sense was already tingling with monocle-mustache, but this nesting is another turn for the worse.
Dave, if nesting were out of the question and monocle-mustache operator always looked like an object literal as they currently exist, would it still be as vile? With that form, I'm a big fan.
/
On Nov 11, 2011, at 4:18 PM, Rick Waldron wrote:
Dave, if nesting were out of the question and monocle-mustache operator always looked like an object literal as they currently exist, would it still be as vile? With that form, I'm a big fan.
I'm of multiple minds (a condition I'm gradually getting accustomed to).
-
I'm really against the current syntax, because it hides the fact that it's a mutation. Assignments masquerading as declarative forms bring out Angry Dave (you wouldn't like me when I'm angry...)
-
I like it better if it includes "=" in it, e.g.:
obj .= { foo: 1, bar: 2 };
-
But that makes me really wish for a more general expression form, so I could also do e.g.:
obj1 .= obj2;
I've even written up a strawman for it:
http://wiki.ecmascript.org/doku.php?id=strawman:batch_assignment_operator
-
Allen has pointed out that this is problematic for private names. For many use cases, it'd be fine if .= just didn't copy any private names -- done. But Allen's class pattern wanted to be able to specify private names. So he restricted the RHS to look like a literal.
-
Still, I have to say, the restricted RHS just seems ad hoc and unsatisfying.
Anyway, I'm still mulling.
While I was admiring the pros of its mutation, I overlooked how unclear it was that a mutation was even occurring... Anyway, thanks for the clarification.
/
On Nov 11, 2011, at 5:22 PM, David Herman wrote:
On Nov 11, 2011, at 4:18 PM, Rick Waldron wrote:
Dave, if nesting were out of the question and monocle-mustache operator always looked like an object literal as they currently exist, would it still be as vile? With that form, I'm a big fan.
I'm of multiple minds (a condition I'm gradually getting accustomed to).
- I'm really against the current syntax, because it hides the fact that it's a mutation. Assignments masquerading as declarative forms bring out Angry Dave (you wouldn't like me when I'm angry...)
It's not clear that it hides mutation any more than ++x or even +x (in the later case where the value of x in an object with a valueOf method that mutates the object). In general, you have to know what syntax means to understand it. .{ is new syntax that is defined to mean mutation. It isn't masquerading as anything.
I like it better if it includes "=" in it, e.g.:
obj .= { foo: 1, bar: 2 };
= really isn't right either because obj.={foo:1,bar: 2};
definitely would not mean the same thing as: obj.foo = 1; obj.bar = 1;
The former does [[DefineOwnProperty]] and allies all the object literal static and dynamic semantics. The later is just a sequence of [[Put]]s.
I've suggested obj.+{foo:1,bar: 2}; as a possible alternative if people really want a stronger lexical distinction.
But that makes me really wish for a more general expression form, so I could also do e.g.:
obj1 .= obj2;
I've even written up a strawman for it:
your strawman doesn't provide any semantics...
- Allen has pointed out that this is problematic for private names. For many use cases, it'd be fine if .= just didn't copy any private names -- done. But Allen's class pattern wanted to be able to specify private names. So he restricted the RHS to look like a literal.
.{ would be useless if it did not propagate private names. Also, if it did not do super binding any all the other static and dynamic semantics of object literals.
It may not be obvious, but note that these are intended to be essentially equivalent expressions:
let obj1 = (new Object).{ /* ...* / }; let obj2 = {/* ... */};
The properties in the mustache are processed as if they were an object literal where the LHS object was the object that other wise would have been created as the first step of processing the object literal.
- Still, I have to say, the restricted RHS just seems ad hoc and unsatisfying.
It's essential for the only use case I care about.
On Nov 12, 2011, at 9:48 AM, Allen Wirfs-Brock wrote:
On Nov 11, 2011, at 5:22 PM, David Herman wrote:
On Nov 11, 2011, at 4:18 PM, Rick Waldron wrote:
Dave, if nesting were out of the question and monocle-mustache operator always looked like an object literal as they currently exist, would it still be as vile? With that form, I'm a big fan.
I'm of multiple minds (a condition I'm gradually getting accustomed to).
- I'm really against the current syntax, because it hides the fact that it's a mutation. Assignments masquerading as declarative forms bring out Angry Dave (you wouldn't like me when I'm angry...)
It's not clear that it hides mutation any more than ++x
Well-known C idiom.
or even +x (in the later case where the value of x in an object with a valueOf method that mutates the object).
Oh come on -- any such mutating valueOf is abnormal in the extreme, a stunt a la tobeytailor/def.js.
In general, you have to know what syntax means to understand it. .{ is new syntax that is defined to mean mutation. It isn't masquerading as anything.
That's the counter-argument: new idiom. JS is a bit mature to be adding such things. Part of the "Grawlix" immune system reaction is not just to Perl-ish line noise -- it's to new idioms that do not build on existing forms so as to support intuition about meaning. In that light, .= is much better (private name issue aside).
I like it better if it includes "=" in it, e.g.:
obj .= { foo: 1, bar: 2 };
= really isn't right either because obj.={foo:1,bar: 2};
definitely would not mean the same thing as: obj.foo = 1; obj.bar = 1;
The former does [[DefineOwnProperty]] and allies all the object literal static and dynamic semantics. The later is just a sequence of [[Put]]s.
Right. .= is not exactly right either.
Some wanted += but it's "taken" (by conversion to string or other primitive and + in the expansion), at least pending user-defined operators.
I've suggested obj.+{foo:1,bar: 2}; as a possible alternative if people really want a stronger lexical distinction.
No intuition aids there -- if we do a new idiom, let's minimize it as you have, to .{ without a + in the middle (which is misleading at best).
- Allen has pointed out that this is problematic for private names. For many use cases, it'd be fine if .= just didn't copy any private names -- done. But Allen's class pattern wanted to be able to specify private names. So he restricted the RHS to look like a literal.
.{ would be useless if it did not propagate private names. Also, if it did not do super binding any all the other static and dynamic semantics of object literals.
The motivating use-case for all that, IINM, is the classless class-as-sugar pattern, which I think is not enough to kill classes. If we do classes, what .{ use-cases remain?
- Still, I have to say, the restricted RHS just seems ad hoc and unsatisfying.
It's essential for the only use case I care about.
For the classless classes-as-sugar pattern? Enquiring minds want to know ;-).
[Apologies for interrupting...]
If we do classes, what .{ use-cases remain?
The operator allows you to use object literals (including all of the niceties that will be introduced in ES.next) where you previously had to do multiple assignments. It’s especially nice for adding things to prototypes. For example:
function Person(name) {
this.name = name;
}
Person.prototype . {
describe() {
return "Person called "+this.name;
}
};
var Employee = Person <| function (name, title) {
super.constructor(name);
this.title = title;
}
Employee.prototype . {
describe() {
return super.describe() + " (" + this.title + ")";
}
};
The power of object literals will grow and the monocle operator allows us to use this power in many more places.
If find that the above pretty enough that I could make do without class declarations.
2011/11/11 Erik Arvidsson <erik.arvidsson at gmail.com>:
We've all looked at jQuery code and envied the conciseness of its chaining APIs. Most of us also looked at it and thought; Yuk, making everything a method of jQuery and always return the jQuery object is ugly.
However, I (with some help) realized that the .{ operator can be used to solve most of these chaining scenarios without any changes to any API.
Take this jQuery sample code:
$('#my-element').
css({
'color': 'red',
'padding': '5px'
}).
text('Hello');
With the 'stache:
document.querySelector('#my-element').{
style.{
'color': 'red',
'padding': '5px'
}.
textContent: 'Hello'
};
One could easily see some extensions to make this even more useful. For example allowing method calls and member lookups etc would sweeten the deal. Here is a more advanced example that assumes we have some useful properties on the returned DOM collections: jQuery:
$('#myTable').
find('.firstColumn').
css({'background': 'red', 'border': '2px solid black'}).
text('first column').
end().
find('.lastColumn').
css('background','blue').
text('last column');
With the 'stache:
document.querySelector('#myTable').{
querySelectorAll('.firstColumn').{
style.{background: 'red', border: '2px solid black'},
textContent: 'first column'
},
querySelectorAll('.lastColumn').{
style.{background: 'blue', border: '2px solid black'},
textContent: 'last column'
}
};
I've also been told that this is just another form of Smalltalk's message cascade.
-- erik
Does this depends on object key iteration order being defined as insertion order?
Since an object can only have one property with a given name can chaining express patterns that the mustache can't? I.e. how would a chain with two calls to methods with the same name translate: foo.bar(x).baz(y).bar(z) ?
On Nov 12, 2011, at 12:22 PM, Axel Rauschmayer wrote:
[Apologies for interrupting...]
No problem!
If we do classes, what .{ use-cases remain?
The operator allows you to use object literals (including all of the niceties that will be introduced in ES.next) where you previously had to do multiple assignments. It’s especially nice for adding things to prototypes. For example:
function Person(name) { this.name = name; } Person.prototype . { describe() { return "Person called "+this.name; } };
That's a good point -- it preserves .constructor, unlike assigning a fresh object literal to Person.prototype.
More:
function Point(x, y) { this.{x,y}; }
function Monster(name) { this.{ name: name, @strength: 10, @age: 0 }; }
// namespace exists in multiple files (possibly less relevant with modules) namespace.{ function1() { }, function2() { } }
On Nov 12, 2011, at 1:20 PM, Axel Rauschmayer wrote:
More:
function Point(x, y) { this.{x,y}; }
Ok, maybe. But
function Monster(name) { this.{ name: name,
just name here should do, not name: name -- right?
@strength: 10, @age: 0 };
}
Someone had to declare private strength, age; first.
// namespace exists in multiple files (possibly less relevant with modules) namespace.{ function1() { }, function2() { } }
This caused a double-take because of the leading 'function' names distinguished only by trailing "1" vs "2". There is something to the complaints about method definition syntax being too terse in certain applications. Just changing to method1 and method2 helps, though.
Maybe that's part of the whole Grawlix thing. o.{m(){}} vs. Object.extend(o, {m: function(){}}). The latter is JS we've known for many years.
function Point(x, y) { this.{x,y}; }
Ok, maybe. But
function Monster(name) { this.{ name: name,
just name here should do, not name: name -- right?
Ah, I thought one couldn’t mix the key-value form and the value-less syntactic sugar. But it makes sense to allow that.
@strength: 10, @age: 0 };
}
Someone had to declare private strength, age; first.
Correct, implied. I was too lazy to look up the import statement, but actually, object literals should have a “private” section, too. And one could allow these sections anywhere.
// namespace exists in multiple files (possibly less relevant with modules) namespace.{ function1() { }, function2() { } }
This caused a double-take because of the leading 'function' names distinguished only by trailing "1" vs "2". There is something to the complaints about method definition syntax being too terse in certain applications. Just changing to method1 and method2 helps, though.
Right, they are technically methods, but “semantically” functions.
Maybe that's part of the whole Grawlix thing. o.{m(){}} vs. Object.extend(o, {m: function(){}}). The latter is JS we've known for many years.
Here is what it looks like with .= (I still think it’s intuitive, even though its functionality goes beyond mere batch property assignments).
function Point(x, y) {
this .= {x,y};
}
// namespace exists in multiple files (possibly less relevant with modules)
namespace .= {
func1() {
},
func2() {
}
}
// Allow anywhere: syntactic sugar for
// module name from "@name";
// let strength = name.create(), age = name.create();
private { strength, age }
function Monster(name) {
this .= {
name,
@strength: 10,
@age: 0
};
}
Unexpectedly, the classic constructor pattern plus .{, <|, super, and abbreviated method syntax is now my favorite class pattern.
We've all looked at jQuery code and envied the conciseness of its chaining APIs. Most of us also looked at it and thought; Yuk, making everything a method of jQuery and always return the jQuery object is ugly.
However, I (with some help) realized that the .{ operator can be used to solve most of these chaining scenarios without any changes to any API.
Take this jQuery sample code:
$('#my-element').
css({
}).
text('Hello');
With the 'stache:
document.querySelector('#my-element').{
style.{
}.
textContent: 'Hello' };
One could easily see some extensions to make this even more useful. For example allowing method calls and member lookups etc would sweeten the deal. Here is a more advanced example that assumes we have some useful properties on the returned DOM collections:
jQuery:
$('#myTable'). find('.firstColumn'). css({'background': 'red', 'border': '2px solid black'}). text('first column'). end(). find('.lastColumn'). css('background','blue'). text('last column');
With the 'stache:
document.querySelector('#myTable').{ querySelectorAll('.firstColumn').{ style.{background: 'red', border: '2px solid black'}, textContent: 'first column' }, querySelectorAll('.lastColumn').{ style.{background: 'blue', border: '2px solid black'}, textContent: 'last column' } };
I've also been told that this is just another form of Smalltalk's message cascade.