Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.javascript > #30587 > unrolled thread

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

Started byStefan Weiss <krewecherl@gmail.com>
First post2016-05-31 14:38 +0200
Last post2016-06-02 15:51 -0700
Articles 8 — 3 participants

Back to article view | Back to comp.lang.javascript

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: not "autovivification", but ...? Stefan Weiss <krewecherl@gmail.com> - 2016-05-31 14:38 +0200
    Re: not "autovivification", but ...? "Michael Haufe (TNO)" <tno@thenewobjective.com> - 2016-06-01 08:03 -0700
      Re: not "autovivification", but ...? Stefan Weiss <krewecherl@gmail.com> - 2016-06-01 19:49 +0200
        Re: not "autovivification", but ...? "Michael Haufe (TNO)" <tno@thenewobjective.com> - 2016-06-01 14:29 -0700
          Re: not "autovivification", but ...? Stefan Weiss <krewecherl@gmail.com> - 2016-06-02 01:29 +0200
            Re: not "autovivification", but ...? Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2016-06-02 02:16 +0200
              Re: not "autovivification", but ...? Stefan Weiss <krewecherl@gmail.com> - 2016-06-02 13:20 +0200
            Re: not "autovivification", but ...? "Michael Haufe (TNO)" <tno@thenewobjective.com> - 2016-06-02 15:51 -0700

#30587 — Re: not "autovivification", but ...?

FromStefan Weiss <krewecherl@gmail.com>
Date2016-05-31 14:38 +0200
SubjectRe: not "autovivification", but ...?
Message-ID<nik0k5$8mu$1@news.albasani.net>
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

[toc] | [next] | [standalone]


#30594

From"Michael Haufe (TNO)" <tno@thenewobjective.com>
Date2016-06-01 08:03 -0700
Message-ID<1dc7bd11-343d-4cbb-b75d-1db8516666a0@googlegroups.com>
In reply to#30587
On Tuesday, May 31, 2016 at 7:38:35 AM UTC-5, Stefan Weiss wrote:
> 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.

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>

[toc] | [prev] | [next] | [standalone]


#30596

FromStefan Weiss <krewecherl@gmail.com>
Date2016-06-01 19:49 +0200
Message-ID<nin783$9qu$1@news.albasani.net>
In reply to#30594
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

[toc] | [prev] | [next] | [standalone]


#30597

From"Michael Haufe (TNO)" <tno@thenewobjective.com>
Date2016-06-01 14:29 -0700
Message-ID<d7cf47ca-3871-46e0-8434-b17ec1fe1440@googlegroups.com>
In reply to#30596
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.

[toc] | [prev] | [next] | [standalone]


#30598

FromStefan Weiss <krewecherl@gmail.com>
Date2016-06-02 01:29 +0200
Message-ID<ninr5k$gee$1@news.albasani.net>
In reply to#30597
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.

[toc] | [prev] | [next] | [standalone]


#30602

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2016-06-02 02:16 +0200
Message-ID<6429329.X1ROTxqqWj@PointedEars.de>
In reply to#30598
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)
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.

[toc] | [prev] | [next] | [standalone]


#30609

FromStefan Weiss <krewecherl@gmail.com>
Date2016-06-02 13:20 +0200
Message-ID<nip4ps$vhs$1@news.albasani.net>
In reply to#30602
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

[toc] | [prev] | [next] | [standalone]


#30623

From"Michael Haufe (TNO)" <tno@thenewobjective.com>
Date2016-06-02 15:51 -0700
Message-ID<3c790fb8-db3c-4ce0-a990-9567b84154e6@googlegroups.com>
In reply to#30598
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.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.javascript


csiph-web