'void' as a value

Brendan Eich brendan at mozilla.com
Sun Sep 8 12:39:20 PDT 2013


Lately people post overlong proposals that do not define the proposed 
primitives first. Also errors in the sketching don't help credibility.

> Andrew Fedoniouk <mailto:news at terrainformatica.com>
> September 8, 2013 11:24 AM
> Consider this function:
>
> function foo( t ) {
> if( t ) return undefined;
> // no return here, sic!
> }
>
> As you see it returns 'undefined' when 't' gets true.
> When 't' is false it also returns the same 'undefined'.
>
> But conceptually these are two different return cases/values:
> 'undefined' and 'nothing' (or 'void').
>
> Allowing functions to return 'void' values will give us opportunity
> to use ordinary functions as iterators without need of separate 
> Iterator entity.

You mean separate iteration protocol? This has been considered, but 
first some problems.
>
> Consider this Range implementation:
>
> function Range(low, high){
> var index = low;
> return function() { if( index < high ) return index++; }
> }
>
> and its use:
>
> for(var i in Range(0,10) )

You must mean for (var i of Range(0, 10)) here, because for-in cannot be 
changed to iterate without breaking backward compatibility.

> ...
>
> Internal implementation of for..in is calling the function in each 
> iteration
> and stops when the function will return 'void'.

Assuming you mean for-of, the problems for any "new undefined-like 
sentinel" remain:

* It will be too easily returned by accident, especially given how it is 
hard to distinguish from undefined.

* It may end up in collections being iterated over, causing premature 
termination.

Any in-band value is a problem by the latter point. Thus the previous 
Pythonic out-of-band StopIteration exception, or the current ES6 
wrapping of iteration protocol step results in {value, done} structure 
to avoid overloading the returned value and creating a pigeon hole problem.
>
> To keep backward compatibility the 'void' value shall not be assignable,
> so after this:
>
> function returnsNothing() { return; }
>
> var retval = returnsNothing();
>
> the retval will still contain 'undefined' value.

This will make very hard-to-detect bugs, by the first bullet point above.
>
> And expression:
> returnsNothing() === undefined
> yields 'true'.
>
> Therefore the 'void' value is mostly for internal use.
>
> But in some cases user space code may also be interested in
> checking returns for 'void' values. In this case we can provide
> checkVoid(probe,valOrCallback) primitive:
>
> var result = checkVoid( returnsNothing(), "nothing" );
>
> in this case the result will get "nothing" value. If valOrCallback is 
> a function
> then it will be invoked to indicate 'void' value:
>
> var gotNothing = false;
> var result = checkVoid( returnsNothing(), function() { gotNothing = 
> true } );
>
> My pardon if something similar to this was already discussed by the group.

We have discussed in-band sentinels (including where the 
iterator-creator can choose the sentinel), out-of-band exceptions, and 
finally the chosen ES6 solution: out-of-band result wrapping with a done 
flag, many times.

In no case does anyone that I've spoken to, on TC39 or anywhere else 
around this planet, want *yet another* bottom type and singleton value a 
la null and undefined. No one. Those who speak Spanish and nearby 
languages tend to say "¡Basta!" -- I'm not kidding :-|.

/be
>
> I am using this mechanism in my TIScript and found it quite useful and 
> effective
> for implementing iterator cases - at least no need for exceptions as 
> shown here:
> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
>


More information about the es-discuss mailing list