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


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

Cheap runtime asserts

Started byglathoud <glathoud@yahoo.fr>
First post2012-12-04 22:00 -0800
Last post2012-12-11 00:07 -0800
Articles 12 — 5 participants

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


Contents

  Cheap runtime asserts glathoud <glathoud@yahoo.fr> - 2012-12-04 22:00 -0800
    Re: Cheap runtime asserts Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2012-12-05 20:18 +0100
      Re: Cheap runtime asserts glathoud <glathoud@yahoo.fr> - 2012-12-06 11:19 -0800
        Re: Cheap runtime asserts Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2012-12-06 23:41 +0100
    Re: Cheap runtime asserts Patricia Shanahan <pats@acm.org> - 2012-12-05 12:00 -0800
      Re: Cheap runtime asserts glathoud <glathoud@yahoo.fr> - 2012-12-06 11:34 -0800
    Re: Cheap runtime asserts JJ <jaejunks@glegooilma-swapit.com> - 2012-12-06 07:29 +0000
      Re: Cheap runtime asserts glathoud <glathoud@yahoo.fr> - 2012-12-06 11:40 -0800
        Re: Cheap runtime asserts JJ <jaejunks@glegooilma-swapit.com> - 2012-12-07 12:03 +0000
          Re: Cheap runtime asserts glathoud <glathoud@yahoo.fr> - 2012-12-10 23:46 -0800
    Re: Cheap runtime asserts Stefan Weiss <krewecherl@gmail.com> - 2012-12-06 23:00 +0100
      Re: Cheap runtime asserts glathoud <glathoud@yahoo.fr> - 2012-12-11 00:07 -0800

#17473 — Cheap runtime asserts

Fromglathoud <glathoud@yahoo.fr>
Date2012-12-04 22:00 -0800
SubjectCheap runtime asserts
Message-ID<1892433a-7696-4b03-a94c-0b1f0a984134@googlegroups.com>
Hello,

To check argument types in functions that are used in many different
places, I have been using this:

function someCoreFunction( domnode, text ) {

    // "Cheap runtime assert": fails when `domnode` is not a DOM node.
    domnode.childNodes.a;
         
    // Same idea, for a string.
    text.substring.a;

    // Rest of the function
    // ...

}

It helps to catch mistakes early, including during development. I am
*not* advocating to litter the code with thousands such statements,
but in a few strategical places the benefits can exceed the costs.

Hopefully this can help someone. Constructive feedback is welcome.

Guillaume

[toc] | [next] | [standalone]


#17499

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2012-12-05 20:18 +0100
Message-ID<5675043.5vPKIy9VtG@PointedEars.de>
In reply to#17473
glathoud wrote:
^^^^^^^^
Please fix that.

> To check argument types in functions that are used in many different
> places, I have been using this:
> 
> function someCoreFunction( domnode, text ) {
> 
>     // "Cheap runtime assert": fails when `domnode` is not a DOM node.
>     domnode.childNodes.a;
> 
>     // Same idea, for a string.
>     text.substring.a;
>  
>     // Rest of the function
>     // ...
> 
> }
> 
> It helps to catch mistakes early, including during development. I am
> *not* advocating to litter the code with thousands such statements,
> but in a few strategical places the benefits can exceed the costs.
> 
> Hopefully this can help someone. Constructive feedback is welcome.

This approach is not going to work; IOW, it is *too* cheap.  In order to let 
the program continue running, instead of bothering the *user* with runtime 
errors and then end prematurely, you will have to catch the TypeError that 
is thrown when execution cannot resolve the “a” property of “undefined”.  
There is no reasonable, cross-implementation way in which you can tell one 
TypeError from another in a program.

Further, you are potentially dealing with host objects here.  It is a bad 
idea to access host objects' properties without testing them for existence 
first.  In the worst case the *successful* property access will throw an 
exception.

Your experience may not be sufficient to know that a runtime error occurs 
then, but it does, and the user will probably see it (for example, Internet 
Explorer will display an exclamation mark icon in the status bar left-hand 
side).  The program will not end cleanly although that could be done, which 
is a really bad idea in itself.

The proper and only reliable way is to check passed arguments against the 
function requirements, and throw a user-defined exception.  Or return a 
false-value, if feasible, so that subsequent uses of the return value fail 
and the problem can be tracked down to the incorrect method call).  See 
”typeof” and “isHostMethod”, and “jsx.throwThis()” for that.


HTH

PointedEars
-- 
Sometimes, what you learn is wrong. If those wrong ideas are close to the 
root of the knowledge tree you build on a particular subject, pruning the 
bad branches can sometimes cause the whole tree to collapse.
  -- Mike Duffy in cljs, <news:Xns9FB6521286DB8invalidcom@94.75.214.39>

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


#17533

Fromglathoud <glathoud@yahoo.fr>
Date2012-12-06 11:19 -0800
Message-ID<87188bbf-3cc7-4926-8154-9a8b7720991e@googlegroups.com>
In reply to#17499
The intent is precisely to crash early in cases where the consequences will *surely* lead to a *non-recoverable* crash, that is unacceptable in production. 

Goal: fail early rather than later, to reduce or prevent backtracking a silent mistake to its origin, especially - but not only - in asynchronous context.

Certainly not all cases are like this, hence the warning at the end of my original post.

Guillaume

On Wednesday, December 5, 2012 8:18:45 PM UTC+1, Thomas 'PointedEars' Lahn wrote:
> glathoud wrote:
> 
> ^^^^^^^^
> 
> Please fix that.
> 
> 
> 
> > To check argument types in functions that are used in many different
> 
> > places, I have been using this:
> 
> > 
> 
> > function someCoreFunction( domnode, text ) {
> 
> > 
> 
> >     // "Cheap runtime assert": fails when `domnode` is not a DOM node.
> 
> >     domnode.childNodes.a;
> 
> > 
> 
> >     // Same idea, for a string.
> 
> >     text.substring.a;
> 
> >  
> 
> >     // Rest of the function
> 
> >     // ...
> 
> > 
> 
> > }
> 
> > 
> 
> > It helps to catch mistakes early, including during development. I am
> 
> > *not* advocating to litter the code with thousands such statements,
> 
> > but in a few strategical places the benefits can exceed the costs.
> 
> > 
> 
> > Hopefully this can help someone. Constructive feedback is welcome.
> 
> 
> 
> This approach is not going to work; IOW, it is *too* cheap.  In order to let 
> 
> the program continue running, instead of bothering the *user* with runtime 
> 
> errors and then end prematurely, you will have to catch the TypeError that 
> 
> is thrown when execution cannot resolve the “a” property of “undefined”.  
> 
> There is no reasonable, cross-implementation way in which you can tell one 
> 
> TypeError from another in a program.
> 
> 
> 
> Further, you are potentially dealing with host objects here.  It is a bad 
> 
> idea to access host objects' properties without testing them for existence 
> 
> first.  In the worst case the *successful* property access will throw an 
> 
> exception.
> 
> 
> 
> Your experience may not be sufficient to know that a runtime error occurs 
> 
> then, but it does, and the user will probably see it (for example, Internet 
> 
> Explorer will display an exclamation mark icon in the status bar left-hand 
> 
> side).  The program will not end cleanly although that could be done, which 
> 
> is a really bad idea in itself.
> 
> 
> 
> The proper and only reliable way is to check passed arguments against the 
> 
> function requirements, and throw a user-defined exception.  Or return a 
> 
> false-value, if feasible, so that subsequent uses of the return value fail 
> 
> and the problem can be tracked down to the incorrect method call).  See 
> 
> ”typeof” and “isHostMethod”, and “jsx.throwThis()” for that.
> 
> 
> 
> 
> 
> HTH
> 
> 
> 
> PointedEars
> 
> -- 
> 
> Sometimes, what you learn is wrong. If those wrong ideas are close to the 
> 
> root of the knowledge tree you build on a particular subject, pruning the 
> 
> bad branches can sometimes cause the whole tree to collapse.
> 
>   -- Mike Duffy in cljs, <news:Xns9FB6521286DB8invalidcom@94.75.214.39>

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


#17538

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2012-12-06 23:41 +0100
Message-ID<5771137.N9fZnj88gR@PointedEars.de>
In reply to#17533
glathoud wrote:

> The intent is precisely to crash early in cases where the consequences
> will *surely* lead to a *non-recoverable* crash, that is unacceptable in
> production.

AISB, the approach will crash also in those cases where the consequences of 
using the asserted feature will _not_ (surely) lead to a non-recoverable 
crash.  Therefore, it is nonsense.
 
Please do not top-post, and do get a real name.

-- 
PointedEars

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


#17501

FromPatricia Shanahan <pats@acm.org>
Date2012-12-05 12:00 -0800
Message-ID<jNWdnUip1a1tOiLNnZ2dnUVZ_oadnZ2d@earthlink.com>
In reply to#17473
On 12/4/2012 10:00 PM, glathoud wrote:
> Hello,
>
> To check argument types in functions that are used in many different
> places, I have been using this:
>
> function someCoreFunction( domnode, text ) {
>
>      // "Cheap runtime assert": fails when `domnode` is not a DOM node.
>      domnode.childNodes.a;
...

One problem I have with this is that childNodes seems like a good name
for the children of a node in any tree with an arbitrary fan-out,
regardless of whether it has anything to do with DOM. What advantage do
you see to this compared to checking the type?

Patricia

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


#17534

Fromglathoud <glathoud@yahoo.fr>
Date2012-12-06 11:34 -0800
Message-ID<4eed6d84-a358-4fd7-8e38-4a61730d73d6@googlegroups.com>
In reply to#17501
On Wednesday, December 5, 2012 9:00:54 PM UTC+1, Patricia Shanahan wrote:
> On 12/4/2012 10:00 PM, glathoud wrote:
> 
> > Hello,
> 
> >
> 
> > To check argument types in functions that are used in many different
> 
> > places, I have been using this:
> 
> >
> 
> > function someCoreFunction( domnode, text ) {
> 
> >
> 
> >      // "Cheap runtime assert": fails when `domnode` is not a DOM node.
> 
> >      domnode.childNodes.a;
> 
> ...
> 
> 
> 
> One problem I have with this is that childNodes seems like a good name
> 
> for the children of a node in any tree with an arbitrary fan-out,
> 
> regardless of whether it has anything to do with DOM. 

Yes, you are perfectly right. There is absolutely no claim of generality. The goal is not to catch all mistakes, but rather to have a good chance to catch strategic mistakes early with minimal effort/cost, so as to reduce or prevent backtracking the mistake.

Note that you *might* reduce the chance of a worst case false negative using e.g. domnode.getBoundingClientRect.a; but then again that's long to type :)

> What advantage do
> 
> you see to this compared to checking the type?

You can target in quite specific way, using a very small amount of code.
In the DOM node case typeof document.createElement('div') may well return 'object',
which is not very specific.

You could use the approach on your own user-defined objects as well, not just on DOM nodes and strings.

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


#17513

FromJJ <jaejunks@glegooilma-swapit.com>
Date2012-12-06 07:29 +0000
Message-ID<XnsA121943364577jaejunksglegooilma@0.0.0.23>
In reply to#17473
glathoud <glathoud@yahoo.fr> wrote:
> To check argument types in functions that are used in many different
> places, I have been using this:
> 
> function someCoreFunction( domnode, text ) {
> 
>     // "Cheap runtime assert": fails when `domnode` is not a DOM node.
>     domnode.childNodes.a;
>          
>     // Same idea, for a string.
>     text.substring.a;
> 
>     // Rest of the function
>     // ...
> 
> }
> 
> It helps to catch mistakes early, including during development. I am
> *not* advocating to litter the code with thousands such statements,
> but in a few strategical places the benefits can exceed the costs.
> 
> Hopefully this can help someone. Constructive feedback is welcome.
> 
> Guillaume

Some conditions might still won't cause an exception. For example:

function theFunc(node, txt) {
  //the cheap assert (pre-access the property)
  node.childNodes[0].data;
  //actual code here
  if (node.childNodes[0].data === '') {
    node.childNodes[0].data = txt;
  }
}
theFunc(document.body);

Now, assuming that "document.childNodes[0]" is a HTMLDivElement, the 
assert won't cause an exception and the next "if..." statement will be 
executed.

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


#17535

Fromglathoud <glathoud@yahoo.fr>
Date2012-12-06 11:40 -0800
Message-ID<cfb82688-a712-4e52-9ec3-ed7f0697320c@googlegroups.com>
In reply to#17513
On Thursday, December 6, 2012 8:29:48 AM UTC+1, JJ wrote:
> glathoud <glathoud@yahoo.fr> wrote:
> 
> > To check argument types in functions that are used in many different
> 
> > places, I have been using this:
> 
> > 
> 
> > function someCoreFunction( domnode, text ) {
> 
> > 
> 
> >     // "Cheap runtime assert": fails when `domnode` is not a DOM node.
> 
> >     domnode.childNodes.a;
> 
> >          
> 
> >     // Same idea, for a string.
> 
> >     text.substring.a;
> 
> > 
> 
> >     // Rest of the function
> 
> >     // ...
> 
> > 
> 
> > }
> 
> > 
> 
> > It helps to catch mistakes early, including during development. I am
> 
> > *not* advocating to litter the code with thousands such statements,
> 
> > but in a few strategical places the benefits can exceed the costs.
> 
> > 
> 
> > Hopefully this can help someone. Constructive feedback is welcome.
> 
> > 
> 
> > Guillaume
> 
> 
> 
> Some conditions might still won't cause an exception. For example:
> 
> 
> 
> function theFunc(node, txt) {
> 
>   //the cheap assert (pre-access the property)
> 
>   node.childNodes[0].data;
> 
>   //actual code here
> 
>   if (node.childNodes[0].data === '') {
> 
>     node.childNodes[0].data = txt;
> 
>   }
> 
> }
> 
> theFunc(document.body);
> 
> 
> 
> Now, assuming that "document.childNodes[0]" is a HTMLDivElement, the 
> 
> assert won't cause an exception and the next "if..." statement will be 
> 
> executed.

Yes, you are right. The goal is not to catch 100% of the mistakes, but to have a good chance to catch strategic mistakes earls (see my answer to Patricia).

In the case you are describing, you could instead make sure that the data property is defined IF that is what you need:

node.childNodes[0].data.a;

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


#17559

FromJJ <jaejunks@glegooilma-swapit.com>
Date2012-12-07 12:03 +0000
Message-ID<XnsA122C28BCCF7Fjaejunksglegooilma@0.0.0.23>
In reply to#17535
glathoud <glathoud@yahoo.fr> wrote:
> Yes, you are right. The goal is not to catch 100% of the mistakes, but
> to have a good chance to catch strategic mistakes earls (see my answer
> to Patricia). 
> 
> In the case you are describing, you could instead make sure that the
> data property is defined IF that is what you need: 
> 
> node.childNodes[0].data.a;

Ah, I see what you're trying to do. Nice trick.

But how to do it if a parameter of null and non-null are both accepted by 
a function? For example, a function to toggle (set/clear) an element's 
onclick handler.

function toggleHandler(ele, func) {
  ele.onclick.a;
  func.a;
  ele.onclick = ele.onclick ? null : func;
}
function dummy() {}
//below line would cause an exception.
//it's expected since document doesn't have onclick property
toggleHandler(document, dummy);
//but below line would also cause an exception.
//assuming that document.body.onclick is null before call
toggleHandler(document.body, dummy);

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


#17670

Fromglathoud <glathoud@yahoo.fr>
Date2012-12-10 23:46 -0800
Message-ID<587e3d40-5635-4e67-b7be-b777744f51c1@googlegroups.com>
In reply to#17559
On Friday, December 7, 2012 1:03:08 PM UTC+1, JJ wrote:
> But how to do it if a parameter of null and non-null are both accepted by 
> 
> a function? For example, a function to toggle (set/clear) an element's 
> 
> onclick handler.
> 
> 
> 
> function toggleHandler(ele, func) {
> 
>   ele.onclick.a;
> 
>   func.a;
> 
>   ele.onclick = ele.onclick ? null : func;
> 
> }

In such a case I would probably not use the approach for `func`. Just check `ele`.

I wrote up a bit more about the background leading to the approach: http://glat.info/js.cheap-asserts/

In general it is up to you to decide, for each parameter, whether to "cheap-check", to fully check (like Thomas described) or to leave it as is (e.g. when expecting multiple types and/or using coercion). In the end, this is a matter a cost/benefit analysis.

But in this particular case, you could do this:

function toggleHandler(ele, func) {

  ele.onclick.a;

  func == null  ||  func.call.a;

  ele.onclick = ele.onclick ? null : func;

}

`func == null` covers both `null` and `undefined` cases.

(and using `func.call.a` is a bit more specific to functions than `func.a`).

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


#17536

FromStefan Weiss <krewecherl@gmail.com>
Date2012-12-06 23:00 +0100
Message-ID<k9r4h7$nkc$1@news.albasani.net>
In reply to#17473
On 2012-12-05 07:00, glathoud wrote:
> To check argument types in functions that are used in many different
> places, I have been using this:
> 
> function someCoreFunction( domnode, text ) {
> 
>     // "Cheap runtime assert": fails when `domnode` is not a DOM node.
>     domnode.childNodes.a;
>          
>     // Same idea, for a string.
>     text.substring.a;
[..]

I see two problems with this approach:

1) This appears to be intended primarily for the development phase. Most
other assert-type runtime checks have a centralized on/off switch, in
order to enable or disable debugging aids like this. Not only is there
no way to globally deactivate them, there is also no easy way to find
them (nothing specific to grep for).

On the other hand, if these statements are intended to remain in the
code after development, the potential error messages that the users will
see (and submit as bugs) are not going to be very helpful: "TypeError:
cannot read property a of undefined" - which of the "asserts" in your
example triggered this?

2) This type of statement will cause linting tools and other code
processors to emit warnings (and rightly so). If you intend to use
something like JSLint or Google's Closure compiler, you'll have to sift
through a lot of irrelevant output in order to find the real warnings.

- stefan

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


#17671

Fromglathoud <glathoud@yahoo.fr>
Date2012-12-11 00:07 -0800
Message-ID<c93c368b-748d-43a1-9606-46678ba26418@googlegroups.com>
In reply to#17536
On Thursday, December 6, 2012 11:00:06 PM UTC+1, Stefan Weiss wrote:
> 1) This appears to be intended primarily for the development phase. [...] which of the "asserts" in your example triggered this?
> 
> 2) This type of statement will cause linting tools and other code
> processors to emit warnings (and rightly so). [...] you'll have to sift
> through a lot of irrelevant output in order to find the real warnings.

Yes, good points. If you decide to only check while developing, either delete the "cheap check" lines after developing a few inter-related components to a stable state, or write full checks that are thrown away during build. 

Without disagreeing with you, I feel uneasy with a code that behaves differently during development and on the live website. This is a matter of choosing a methodology. Concerning the second point, I am questioning whether those warnings are "right". Should the tool limit the programmer, or adapt to his needs? Again, this is a matter of choosing a methodology.

Now concerning this:
> he potential error messages that the users will 
> see (and submit as bugs) are not going to be very helpful

either:

the "users" are programmers, and will most likely set their browser to break on error, and thus immediately see that the required value is e.g. `null` or that they wrongly swapped parameters in a function call. They can then correct their call themselves. Without check, they'd most likely see a consequence further down, which they are much less likely to link to a function call they made - then they are actually less likely to fix it themselves, thus more likely to submit a bug report. Another bug, which you'll have to backtrack to its origin.

or:

the "users" are not programmers and are anyway NOT going to debug, but rather to submit a bug description focusing on the use case leading to the error, whatever the error is.

[toc] | [prev] | [standalone]


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


csiph-web