Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: not "autovivification", but ...?

49 views
Skip to first unread message

Christoph M. Becker

unread,
May 31, 2016, 8:11:39 AM5/31/16
to
On 31.05.2016 at 13:51, Stefan Ram wrote:

> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>> "use strict"; this.b = 1;
>
> And I wonder, why in strict mode in the outmost scope, when
>
> b = 1
>
> is forbidden (given that »this.b« does not exist)
>
> this.b = 1
>
> is allowed. What is the rationale for allowing it with »this.«?

`this` is not special in this regard; you can assign to undefined
properties of *any* object.

Also consider that accessing undefined properties of an object yield
`undefined` (but not an error), so it appears to be logical to allow
assigning another value to such undefined properties.

--
Christoph M. Becker

Stefan Weiss

unread,
May 31, 2016, 8:38:35 AM5/31/16
to
Stefan Ram wrote:
> If Perl's autovivification would exist in JavaScript, this
> would mean that in a new interpreter instance, the evaluation
> of the expression
>
> a.b.c = 1;
>
> would implicitly create the object »a« and »a.b«
> and then assign »1« to the property »c« of »a.b«.

Perl's autovivification is a special case. It looks very user-friendly at
first glance, because it can automatically create intermediate structures
when they're needed - but it also does that on access, not just on
assignment. Examining a value can modify it, which is a common source of bugs:

my %hash;
if (exists $hash{foo}{bar}{baz}) {
say "this statement never executes";
}

Looks innocent enough, but %hash has just been changed from `undef` to

( foo => { bar => {} } )

> We do not have this in JavaScript, but we do have
>
> "use strict"; this.b = 1;
>
> . That is, when »this.b« is a assigned to - even in strict mode -
> a new property »b« is implicitly created. No declaration (with
> »var«, »let«, or »const«) is required.
>
> Is there a name for this »feature« of JavaScript?
> Maybe »semivivification«, because it's like
> autovivification, but not so strong?

I would advise against using the name (auto/semi)vivification, if only to
avoid the negative connotations this feature has in Perl (at least for
experienced programmers).

There is a perfectly good description for this behavior in JavaScript, and
you even used it yourself: implicit property creation.

In both languages, there are ways to prevent this implicit creation: the "no
autovivification" pragma in Perl; strict mode and object sealing or freezing
in JS.


- stefan

Thomas 'PointedEars' Lahn

unread,
May 31, 2016, 11:31:42 AM5/31/16
to
Stefan Ram wrote:

> […] we do have
>
> "use strict"; this.b = 1;
>
> . That is, when »this.b« is a assigned to - even in strict mode -
> a new property »b« is implicitly created. No declaration (with
> »var«, »let«, or »const«) is required.
>
> Is there a name for this »feature« of JavaScript?

Property write access. It does not only work with the object referred to by
“this” – which, you should keep in mind, does not have to be so particularly
in strict mode – but with all objects that have the [[Extensible]] flag set.

Also, you want to be careful here: assigning to a property of “this” can
result in the creation of the equivalent of a global variable when a
function in which it is used is not called as a constructor, and it results
in a ReferenceError exception thrown in function context in strict mode if
the function is neither called as the method of an object nor as a
constructor.

“Autovivification” (in C#: null propagation) would certainly be useful at
times (very often you just want to get “undefined” from x.y.z when x.y
yields “undefined” instead of typeof-testing the whole shebang, and
x.y.z = 1 when x.y is “undefined”; consider arrays of objects), and is
available in CoffeeScript as the “accessor variant of the existential
operator“:

x?.y.z

<http://stackoverflow.com/questions/15260732/does-typescript-support-the-operator-and-whats-it-called>

--
PointedEars
FAQ: <http://PointedEars.de/faq> | SVN: <http://PointedEars.de/wsvn/>
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-matrix>
Please do not cc me. / Bitte keine Kopien per E-Mail.

Michael Haufe (TNO)

unread,
Jun 1, 2016, 10:51:07 AM6/1/16
to
On Tuesday, May 31, 2016 at 7:11:39 AM UTC-5, Christoph M. Becker wrote:
> On 31.05.2016 at 13:51, Stefan Ram wrote:
>
> > (Stefan Ram) writes:
> >> "use strict"; this.b = 1;
> >
> > And I wonder, why in strict mode in the outmost scope, when
> >
> > b = 1
> >
> > is forbidden (given that »this.b« does not exist)
> >
> > this.b = 1
> >
> > is allowed. What is the rationale for allowing it with »this.«?
>
> `this` is not special in this regard; you can assign to undefined
> properties of *any* object.

Most, not any. For example: [1]

> Also consider that accessing undefined properties of an object yield
> `undefined` (but not an error), so it appears to be logical to allow
> assigning another value to such undefined properties.

That does not follow. Ex:

//assume this function is defined in a 3rd party library
function getSomeObject(){
return Object.seal({
hello: "world"
})
}

//in your code somewhere:
var someObject = getSomeObject()

typeof someObject.hello // string

typeof someObject.hola // undefined

someObject.hola = "mundo"

typeof someObject.hola // undefined


[1] <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal>

Michael Haufe (TNO)

unread,
Jun 1, 2016, 11:03:55 AM6/1/16
to
IME, multi-level implicit creation is a sign that something is wrong with your architecture. It makes me ask: "Why were these undefined in the first place?"

The dual issue for implicit creation is accessing a nested structure where one or more of the intermediate members may not exist. My same question above applies there as well: Why don't these already exist?

I think the lack of desire to revisit the architecture of the application leads to some interesting choices in how people code:

var foo = (((a || {}).b || {}).c || {}).d

and then helper functions to make that less awkward and more general:

Object.walk = (o, path)=>path.split('.').reduce((o,k)=>o && o[k], o);

var foo = Object.walk(a,"a.b.c.d")

and then language feature attempts to make it standard:

<https://esdiscuss.org/topic/the-existential-operator>

...

Where fundamentally people should take a step back and take a moment to think at a higher level about what they are trying to accomplish.

</end-pseudo-rant>

Christoph M. Becker

unread,
Jun 1, 2016, 12:46:51 PM6/1/16
to
On 01.06.2016 at 16:51, Michael Haufe (TNO) wrote:

> On Tuesday, May 31, 2016 at 7:11:39 AM UTC-5, Christoph M. Becker wrote:
>
>> `this` is not special in this regard; you can assign to undefined
>> properties of *any* object.
>
> Most, not any. For example: [1]
>
>> Also consider that accessing undefined properties of an object yield
>> `undefined` (but not an error), so it appears to be logical to allow
>> assigning another value to such undefined properties.
>
> That does not follow. Ex:
>
> //assume this function is defined in a 3rd party library
> function getSomeObject(){
> return Object.seal({
> hello: "world"
> })
> }
>
> [1] <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal>

Thanks for pointing that out. I most certainly should catch up with ES
5.1 ASAP. :)

--
Christoph M. Becker

Stefan Weiss

unread,
Jun 1, 2016, 1:50:01 PM6/1/16
to
Michael Haufe (TNO) wrote:
> IME, multi-level implicit creation is a sign that something is wrong
> with your architecture. It makes me ask: "Why were these undefined in the first
> place?"
>
> The dual issue for implicit creation is accessing a nested structure
> where one or more of the intermediate members may not exist. My same
> question above applies there as well: Why don't these already exist?
>
> [snip examples of workarounds]
>
> Where fundamentally people should take a step back and take a moment to
> think at a higher level about what they are trying to accomplish.

I sort of agree with this, to a point, but only if I created the data
structure myself (or my application did). In many situations we simply don't
have that level of control over data. Examples for this would be external
APIs, JSON config files, or any other external tree-like structure mapped to
nested objects.

Are you saying that all data exchange formats with optional components are
badly designed, or that at least all known container nodes should be
present? I don't think that's a realistic requirement.


- stefan

Michael Haufe (TNO)

unread,
Jun 1, 2016, 5:29:11 PM6/1/16
to
On Wednesday, June 1, 2016 at 12:50:01 PM UTC-5, Stefan Weiss wrote:

> I sort of agree with this, to a point, but only if I created the data
> structure myself (or my application did). In many situations we simply don't
> have that level of control over data. Examples for this would be external
> APIs, JSON config files, or any other external tree-like structure mapped to
> nested objects.

> Are you saying that all data exchange formats with optional components are
> badly designed,

No. Nothing like that at all.

> or that at least all known container nodes should be
> present? I don't think that's a realistic requirement.

Nor this either.

Let me try to approach this another way to try and clarify my meaning:

<script>
var someObject = JSON.parse(foo);
</script>

If I could rely on the structure of someObject, I could use a definite access pattern:

<script>
var target = someObject.left.right.right.left.value
</script>

We know that's unrealistic in reality and we'd want to do some checking.
We could apply one of the patterns in my last message:

<script>
target = Object.walk(someObject,"someObject.left.right.right.left.value")
</script>

or let's assume the elvis operator was available in the language:

<script>
target = someObject?.left?.right?.right?.left?.value
</script>

Another example with the DOM:

<script>
var target2 = document.body.firstChild.nextSibling.lastChild
</script>

and again with the suggested operator as a more robust alternative:

<script>
target2 = document?.body?.firstChild?.nextSibling?.lastChild
</script>

I assume these two examples strike you as ridiculous, or at least suspect in what it is doing In your own applications, and many you've no doubt run across, you see such a thing not all on one line, but instead spread across methods and function calls as a parameter:

<script>
foo(someObject)

function foo(obj) {
if(obj && obj.left) {
// ...
bar(obj.left)
}
}

function bar(obj) {
if(obj && obj.right) {
//...
baz(obj.right)
}
}

function baz(obj) {
if(el && obj.right) {
//etc...
}
}
</script>

It's the same issue, but less obvious as it's spread across the program.

Now let's backup and look at the DOM example.

Instead of the above, you'd no doubt avoid such probing to find the element you want and would instead use a search of the data structure:

<script>
target2 = document.getElementById("targetNode")
</script>

But you generally don't see this being exercised against arbitary objects (with the exception of JSONPath users and such)

Partly this is due to the lack of such a general facility in JS, but also due to a different mindset when it comes to the DOM vs. JS objects.

If you are able to recognize the objects you deal with as being an instance of one of the well known data structures you can leverage such functionality as searching.

Even if you aren't provided such a structure you could use the Decorator Pattern to wrap that 3rd party structure and provide something more useful for your own portion of the application:

<script>
var data = new MyTreeConstructor(
someThirdPartyObject
);

var results = data.find("something")
result.forEach(...)

</script>

For simpler objects you can use the Decorator Pattern to provide sensible defaults just once.

Stefan Weiss

unread,
Jun 1, 2016, 7:30:01 PM6/1/16
to
Michael Haufe (TNO) wrote:
> On Wednesday, June 1, 2016 at 12:50:01 PM UTC-5, Stefan Weiss wrote:
>
>> I sort of agree with this, to a point, but only if I created the data
>> structure myself (or my application did). In many situations we simply don't
>> have that level of control over data. Examples for this would be external
>> APIs, JSON config files, or any other external tree-like structure mapped to
>> nested objects.
>
>> Are you saying that all data exchange formats with optional components are
>> badly designed,
>
> No. Nothing like that at all.
>
>> or that at least all known container nodes should be
>> present? I don't think that's a realistic requirement.
>
> Nor this either.
>
> Let me try to approach this another way to try and clarify my meaning:

[summarized ways to access nested properties, if I understood correctly:]

a) rely on the structure: someObject.left.right.right.left.value
b) use a custom walk() method (or similar)
c) use a specialized operator (like `?.`)
d) spread across multiple functions
e) use an alternate identifier (id attributes in the DOM)
f) use something like JSONPath
g) use a decorator to provide the search/path feature

While this is a very useful list of techniques, I'm even more confused
now... These are all used when we don't know if a certain branch (or path
component) exists in a nested object. I thought your earlier point was that
this situation shouldn't be allowed to occur in a well-designed application,
but I'm probably misunderstanding something here:

Michael Haufe (TNO) wrote:
>>> The dual issue for implicit creation is accessing a nested structure
>>> where one or more of the intermediate members may not exist. My same
>>> question above applies there as well: Why don't these already exist?
>>>
>>> I think the lack of desire to revisit the architecture of the
>>> application leads to some interesting choices in how people code [...]


I'm not trying to make a case for implicitly *creating* multiple levels in a
nested structure - even where that is supported, I usually go out of my way
to avoid it. See the Perl example in my first reply. But accessing such a
structure is very common and often cannot be avoided. Which of the mentioned
techniques is used to solve this is a matter of preference, IMO.


- stefan


PS, a little off-topic: PHP is probably the worst language to work with in
this regard. It will happily allow implicit multi-level creation in arrays -

// ($foo is unused up to this point)
$foo["bar"][13]["baz"] = "qux";

- but will emit E_NOTICE (a type of error) if we try to read a nonexistent
index ($foo["x"]). Only with the relatively recent release of PHP7 did it
get a null-coalescing operator:

$x = $foo["baz"][14]["bar"] ?? "qux";

And when objects are involved... best not to think about it.

Thomas 'PointedEars' Lahn

unread,
Jun 1, 2016, 8:16:48 PM6/1/16
to
Stefan Weiss wrote:

> PS, a little off-topic: PHP is probably the worst language to work with in
> this regard. It will happily allow implicit multi-level creation in arrays
> -
>
> // ($foo is unused up to this point)
> $foo["bar"][13]["baz"] = "qux";

This is so very useful. (No sarcasm.)

> - but will emit E_NOTICE (a type of error)

A common misonception. It is a kind of debug message instead and can be
suppressed in various ways.

> if we try to read a nonexistent index ($foo["x"]).
^^^^^
The proper term is _key_, and if you write

@$foo["x"]

the notice will go away without any extra configuration.

F'up2 comp.lang.php

--
PointedEars (ZCE PHP)

Stefan Weiss

unread,
Jun 2, 2016, 7:20:34 AM6/2/16
to
Thomas 'PointedEars' Lahn wrote:
> F'up2 comp.lang.php

I don't read comp.lang.php. If you really want to talk about an off-topic
remark, do it here.

> Stefan Weiss wrote:
>
>> PS, a little off-topic: PHP is probably the worst language to work with in
>> this regard. It will happily allow implicit multi-level creation in arrays
>> -
>>
>> // ($foo is unused up to this point)
>> $foo["bar"][13]["baz"] = "qux";
>
> This is so very useful. (No sarcasm.)

Useful, maybe. It's also more dangerous, because it won't warn you about a
typo in one of the keys; PHP will silently build up a parallel structure if
you mistype "bar". I would rather get the early warnings.

>> - but will emit E_NOTICE (a type of error)
>
> A common misonception. It is a kind of debug message instead and can be
> suppressed in various ways.

E_NOTICE is not just emitted for undefined array keys or indices. If you
disable or suppress it, you will no longer be warned about mistyped variable
names, among other things. It's an important tool, and should enabled during
development.

> if you write
>
> @$foo["x"]
>
> the notice will go away without any extra configuration.

And so will the more serious errors. The @ operator suppresses ALL
recoverable errors, not just notices. @$fooo["x"] or @$foo[x] will not
trigger any warnings.

(The @ operator also used to produce a noticable performance overhead in
earlier versions, but this is now negligible.)


- stefan

Michael Haufe (TNO)

unread,
Jun 2, 2016, 6:51:22 PM6/2/16
to
On Wednesday, June 1, 2016 at 6:30:01 PM UTC-5, Stefan Weiss wrote:

> [summarized ways to access nested properties, if I understood correctly:]
>
> a) rely on the structure: someObject.left.right.right.left.value
> b) use a custom walk() method (or similar)
> c) use a specialized operator (like `?.`)
> d) spread across multiple functions
> e) use an alternate identifier (id attributes in the DOM)
> f) use something like JSONPath
> g) use a decorator to provide the search/path feature
>
> While this is a very useful list of techniques, I'm even more confused
> now... These are all used when we don't know if a certain branch (or path
> component) exists in a nested object. I thought your earlier point was that
> this situation shouldn't be allowed to occur in a well-designed application,
> but I'm probably misunderstanding something here:

I'll try to be more succinct. I didn't have enough time to write a shorter explanation [1].

A: This is what some people do which we probably agree is a bad idea because it is brittle and assumes everything is defined.

B, C: To get around the problems of A, helper functions like Object.walk are made and JS language features are proposed.

I claim that B and C are just workarounds for a problem that can be avoided completely, hence making B and C irrelevant. The problem is "Probing" [2]

D: This is the same problem as B & C but in a different form (if-checks). This is also harder to see as it's spread across the program.

If you have a tree/graph like object, you don't have to probe. You can search.
E,F are examples of this.

for other, simpler objects, you can use G as an approach to provide yourself with more sane data to work with.

> Michael Haufe (TNO) wrote:
> >>> The dual issue for implicit creation is accessing a nested structure
> >>> where one or more of the intermediate members may not exist. My same
> >>> question above applies there as well: Why don't these already exist?
> >>>
> >>> I think the lack of desire to revisit the architecture of the
> >>> application leads to some interesting choices in how people code [...]
>
>
> I'm not trying to make a case for implicitly *creating* multiple levels in a
> nested structure - even where that is supported, I usually go out of my way
> to avoid it. See the Perl example in my first reply. But accessing such a
> structure is very common and often cannot be avoided. Which of the mentioned
> techniques is used to solve this is a matter of preference, IMO.

I think my explanation above subsumes this.

HTH

[1] "If I Had More Time, I Would Have Written a Shorter Letter" <http://quoteinvestigator.com/2012/04/28/shorter-letter/>
[2] Probing: I don't know if I can use this property/method, so I better check to see if it exists first.
0 new messages