Existential operator (was: ||= is much needed?)

# Brendan Eich (13 years ago)

Sorry, meant to start a new thread for:

strawman:existential_operator

As the Syntax section hints, we can't also adopt CoffeeScript's ?( variant, which enables foo.bar?(args, go, here).baz and the like. The C syntax heritage prevails.

# Aymeric Vitte (13 years ago)

This is related to what I was trying to figure out in the "more fun with undefined" thread, maybe it is wrong or have too many impact but I was about to suggest :

8.9.1 GetValue (V) ... 5. If IsUnresolvableReference(V), return undefined

11.2.1 Property Accessors Runtime Semantics: Evaluation ... 3. If baseValue is an abrupt completion or undefined, return baseValue.

Why not, instead of adding "?" operator ?

Le 18/06/2012 07:11, Brendan Eich a écrit :

# Brendan Eich (13 years ago)

Aymeric Vitte wrote:

Why not, instead of adding "?" operator ?

You mean ?. here, I assume.

The answer is because what you propose is an incompatible change to ECMA-262 (all editions) and JS back to its birth, which lets programs that throw currently continue to run, with possibly disastrous consequences (undefined flowing into the bank-balance database as NaN). There is no way that this kind of potentially-bug-hiding behavior should be the default semantics. It must be explicitly opted-into via new syntax.

# Aymeric Vitte (13 years ago)

OK, ?. is enough and good

Le 18/06/2012 16:48, Brendan Eich a écrit :

# Allen Wirfs-Brock (13 years ago)

"This proposal augments the default operator by adding syntax to avoid accessing a missing property, specifically ?. for getting and setting a property from a reference base that might be undefined or null:" 1

The specified semantics of proposal actually does more than "avoid accessing a missing property". It also avoids accessing a property of a missing object. For example,

function f() { var foo; //oops, I forgot to write the initializer expression return foo?.bar; //returns undefined because foo evaluates to undefined. foo.bar would throw }

This seems slightly and possibly significantly different from a situation like:

function f(foo={ }) { return foo?.bar; //returns value of foo.bar or undefined if property bar does not exist, just like foo.bar }

In particular, it has the affect of possibly propagating the detection of a simple editorial error like a forgotten initializer further from its place of occurrence.

At the least, this behavior probably should be clarified in the description

The inability to use ?. meaningfully in a function call context would seem to make this a only half useful feature. I suspect many people will initially try to write something like foo?.bar() with the expectation that the call will be skipped if bar doesn't exist.

These may be an alternative semantics that might address both of the above issues. Consider:

The semantics of ?. could be defined in terms of a new Reference variant (a Conditiional Reference): Let baseReference be the result of evaluating MemberExpression. If Type(baseReference) is Reference and IsConditionalReference(baseReference) is true, then let baseValue be GetUnconditionalValueOrMissing(baseReference) Else let baseValue be GetValue(baseReference). If baseValue is null or undefined, then throw a TypeError exception. Let propertyName be the result of evaluating IdentifierName. If the syntactic production that is being evaluated is contained in strict mode code, let strict be true, else let strict be false. Return a value of type Conditional Reference whose base value is baseValue and whose referenced name is propertyName, and whose strict mode flag is strict. Missing is an internal value that designates an attempt to access a missing value.

GetUnconditionalValueOrMissing(R) where R is a Reference is defined as Let value be GetValue(R). If value is null or undefined then if IsConditionalReference(R) is true hen return Missing. Return value.

and GetValue would get the following new steps:

2.5 If IsConditionalReference(V) is true then If base is Missing then then return undefined.

Function call semantics (11.2.3) could then be modified with the existing step 2 replaced with

2.1 Let func be GetUnconditionalValueorMIssing(ref). 2.2 If func is MIssing, then return undefined.

With these changes, the following would evaluate to undefined: var foo ={}, foo2; foo?.bar() but foo.bar(); would throw.

But note that both foo2.bar() foo2?.bar() would throw reference error exceptions because of line 4 of the ?. semantics n (and the existing .). In other words, a variable reference that resolves to undefined or null when used as a base of ?. doesn't be a pass.

However, the two above changes a separable with want one, but not the other.

Allen

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

"This proposal augments the default operator strawman:default_operator by adding syntax to avoid accessing a missing property, specifically |?.| for getting and setting a property from a reference base that might be |undefined| or |null|:" [1]

The specified semantics of proposal actually does more than "avoid accessing a missing property". It also avoids accessing a property of a /missing object. For example,/ / / function f() { var foo; //oops, I forgot to write the initializer expression return foo?.bar; //returns undefined because foo evaluates to undefined. foo.bar would throw }

This seems slightly and possibly significantly different from a situation like:

function f(foo={ }) { return foo?.bar; //returns value of foo.bar or undefined if property bar does not exist, just like foo.bar }

I can't tell if you love it or hate it :-P.

Seriously, are you objecting to the reference to the default operator? I can take that out.

Adopting CoffeeScript's ?. operator is indeed what I'm proposing. Not because of CoffeeScript or anything magical or any argument from authority. Rather, because it has been user-tested there, in a language with JS's runtime semantics (no change to runtime semantics in CS

# David Bruant (13 years ago)

What about a more generic operator that would be able to silently absorb any error?

 let greedy = obj.hints?.greedy;

would become:

 let greedy = silent obj.hints.greedy;

The semantic would be exactly the same. The syntax is up to debate. Specifically, it would be nice to be able to silence a part of an expression. What about a 'try' without a catch?

 let greedy = try obj.hints.greedy
 let greedy = try{ obj.hints.greedy } || 22;

This is currently forbidden (I think for 2 reasons, one being that a try must be followed by a catch or finally and the other that try/catch cannot be used as expressions)

This idea is partly inspired by typeof which silences ReferenceErrors, which I see as a feature YMMV. It is also intended to make programs potentially more robust more easily. For instance, JSON.stringify can throw errors in the rare case of cyclic references. But very few people know that. To make a robust program using JSON.stringify, one would just need to turn:

 let s = JSON.stringify(o)

into

 let s = try{ JSON.stringify(o) } || DEFAULT_VALUE;

instead of the current:

 let s;
 try{
     s = JSON.stringify(o)
 }
 catch(e){
     // I don't care about the error anyway
     s = DEFAULT_VALUE;
 }

David

Le 18/06/2012 07:11, Brendan Eich a écrit :

# Hemanth H.M (13 years ago)

As there is no keyword as 'or' so far, does something like *x = x.value or 5 *sound better?

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 12:44 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

"This proposal augments the default operator strawman:default_operator by adding syntax to avoid accessing a missing property, specifically |?.| for getting and setting a property from a reference base that might be |undefined| or |null|:" [1]

The specified semantics of proposal actually does more than "avoid accessing a missing property". It also avoids accessing a property of a /missing object. For example,/ / / function f() { var foo; //oops, I forgot to write the initializer expression return foo?.bar; //returns undefined because foo evaluates to undefined. foo.bar would throw }

This seems slightly and possibly significantly different from a situation like:

function f(foo={ }) { return foo?.bar; //returns value of foo.bar or undefined if property bar does not exist, just like foo.bar }

I can't tell if you love it or hate it :-P.

Seriously, are you objecting to the reference to the default operator? I can take that out.

I can't tell either.

What I'm concerned about above is the first object reference in a chain of existential operators. I was trying to illustrate a possible issue with your semantics when the existence of a property is conditional but the object that would have the property is expected to exist. For example when foo?.bar is intended to only be conditional on the existence of property "bar" and not on the existence of an object value of foo. It's all a matter of user intent. Are they thinking "access bar if it exists in foo" or are they thinking "access foo.bar if it exists".

Errors such as the missing initializer in my example will be obscured when users are thinking the first way.

This is probably an area where feedback from CoffeeScript usage would be informative. We've heard thatCoffeeScript developer have found ?. to have high utility. I haven't heard anything about possible negative experiences, for example ones such as I'm talking about above. Is this because, in practice, it isn't a problem. Or perhaps nobody has spoken up. So I'm asking. CoffeeScript users: has use of ?. created any problems for you?

Adopting CoffeeScript's ?. operator is indeed what I'm proposing. Not because of CoffeeScript or anything magical or any argument from authority. Rather, because it has been user-tested there, in a language with JS's runtime semantics (no change to runtime semantics in CS -- "it's just syntax"), and found to be "good".

More below, but an issue may be ?. without .() and that combination has not yet been tested and found to be good.

In particular, it has the affect of possibly propagating the detection of a simple editorial error like a forgotten initializer further from its place of occurrence.

Yes, that's why it's not the default behavior of ".", revised incompatibly as Aymeric mooted.

As an opt-in for writing optional objects or maybe-typed chained expressions, it has its proper uses.

No doubt, but as I'm been trying to express it may also present a hazard. I'm just trying to understand the nature and severity of the hazard and to explore whether there are possible mitigations.

At the least, this behavior probably should be clarified in the description

Will do, I revised the semantics but not the leading blurb.

The inability to use ?. meaningfully in a function call context would seem to make this a only half useful feature. I suspect many people will initially try to write something like foo?.bar() with the expectation that the call will be skipped if bar doesn't exist.

That's where CoffeeScript provides foo.bar?(), but of course we can't make that syntax work. I've tried. An ad-hoc backtracking hack is elusive. GLR is not going to fly.

And I'm not suggesting such a syntactic solution. I am suggesting that there is a forward value propagation semantics that would have the desired behavior.

With these changes, the following would evaluate to undefined: var foo ={}, foo2; foo?.bar() but foo.bar(); would throw.

This misplaces the ?. and deviates from CoffeeScript. Writing foo?.bar() means "try to get bar from foo but if foo cannot coerce to object, short-circuit to result in undefined." The ?. replaces . and means get or set, depending on whether the IdentifierName after the ?. is the last component in an lvalue (followe by an =), or part of an rvalue (including an lvalue's base).

You've already deviated from CoffeeScript by not having ?(). Some of the cow paths are now a dead-end, so you need to look at where the cows were going rather than how they got there.

When would anybody every write: foo?.bar() and want the call to throw? If that was what they wanted why not say: foo.bar() My proposal may be different from CoffeeScript, but it would seem to exactly reflect what a programmer is likely to want and my semantic sketch showed how it would work. I don't see anything confusing in my suggest semantics, I see the exact opposite.

The reason CoffeeScript offers ?( as well as ?. is precisely because, given a maybe-null-or-undefined foo.bar, you can not only further ?. your way to a baz and fail soft with undefined -- you might also want to try to invoke foo.bar as a method and fail soft if it is not invocable (typeof foo.bar != "function"). This works for an unqualified identifier foo, too:

Yes, what I'm suggesting doesn't deal with the "only invoke if a function" use case. However, I'm guessing that the "only try to invoke if it exists" use case is far more common. I'm suggesting that my semantics addresses the sweet spot of CoffeeScript combined ?. and ?() usage without having ?()

$ cat /tmp/x.coffee f = null f?() $ ./bin/coffee -p /tmp/x.coffee (function() { var f;

f = null;

if (typeof f === "function") { f(); }

}).call(this);

The basis case of this induction is an unqualified identifier, not something like foo.bar with a dot or question-dot in it. This shows that ?. is not sufficient to avoid a TypeError attempting to invoke a maybe-function. A variant such as ?( is needed after the unqualified identifier.

Right, I think that f?() is a much less interesting use case than foo?.bar?()

Alas, we can't use CoffeeScript's nice spelling, and I don't see another good way to spell it.

I still stand by my alternative semantics as being a way to address the more important use case without have ?()

However, I believe (survey needed) that the ?( variant is much less used than ?. in practice. Cc'ing Jeremy.

That would be good.

The other question would be how often do you see foo?.bar() used intentionally rather than foo?.bar?() Ever?

# Herby Vojčík (13 years ago)

Allen Wirfs-Brock wrote:

On Jun 19, 2012, at 12:44 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

"This proposal augments the default operatorstrawman:default_operator by adding syntax to avoid accessing a missing property, specifically |?.| for getting and setting a property from a reference base that might be |undefined| or |null|:" [1]

The specified semantics of proposal actually does more than "avoid accessing a missing property". It also avoids accessing a property of a /missing object. For example,/ / / function f() { var foo; //oops, I forgot to write the initializer expression return foo?.bar; //returns undefined because foo evaluates to undefined. foo.bar would throw }

This seems slightly and possibly significantly different from a situation like:

function f(foo={ }) { return foo?.bar; //returns value of foo.bar or undefined if property bar does not exist, just like foo.bar } I can't tell if you love it or hate it :-P.

Seriously, are you objecting to the reference to the default operator? I can take that out.

I can't tell either.

What I'm concerned about above is the first object reference in a chain of existential operators. I was trying to illustrate a possible issue with your semantics when the existence of a property is conditional but the object that would have the property is expected to exist. For example when foo?.bar is intended to only be conditional on the existence of property "bar" and not on the existence of an object value of foo. It's all a matter of user intent. Are they thinking "access bar if it exists in foo" or are they thinking "access foo.bar if it exists".

Errors such as the missing initializer in my example will be obscured when users are thinking the first way.

This is probably an area where feedback from CoffeeScript usage would be informative. We've heard thatCoffeeScript developer have found ?. to have high utility. I haven't heard anything about possible negative experiences, for example ones such as I'm talking about above. Is this because, in practice, it isn't a problem. Or perhaps nobody has spoken up. So I'm asking. CoffeeScript users: has use of ?. created any problems for you?

Adopting CoffeeScript's ?. operator is indeed what I'm proposing. Not because of CoffeeScript or anything magical or any argument from authority. Rather, because it has been user-tested there, in a language with JS's runtime semantics (no change to runtime semantics in CS -- "it's just syntax"), and found to be "good".

More below, but an issue may be ?. without .() and that combination has not yet been tested and found to be good.

In particular, it has the affect of possibly propagating the detection of a simple editorial error like a forgotten initializer further from its place of occurrence. Yes, that's why it's not the default behavior of ".", revised incompatibly as Aymeric mooted.

As an opt-in for writing optional objects or maybe-typed chained expressions, it has its proper uses.

No doubt, but as I'm been trying to express it may also present a hazard. I'm just trying to understand the nature and severity of the hazard and to explore whether there are possible mitigations.

At the least, this behavior probably should be clarified in the description Will do, I revised the semantics but not the leading blurb.

The inability to use ?. meaningfully in a function call context would seem to make this a only half useful feature. I suspect many people will initially try to write something like foo?.bar() with the expectation that the call will be skipped if bar doesn't exist. That's where CoffeeScript provides foo.bar?(), but of course we can't make that syntax work. I've tried. An ad-hoc backtracking hack is elusive. GLR is not going to fly.

And I'm not suggesting such a syntactic solution. I am suggesting that there is a forward value propagation semantics that would have the desired behavior.

With these changes, the following would evaluate to undefined: var foo ={}, foo2; foo?.bar() but foo.bar(); would throw. This misplaces the ?. and deviates from CoffeeScript. Writing foo?.bar() means "try to get bar from foo but if foo cannot coerce to object, short-circuit to result in undefined." The ?. replaces . and means get or set, depending on whether the IdentifierName after the ?. is the last component in an lvalue (followe by an =), or part of an rvalue (including an lvalue's base).

You've already deviated from CoffeeScript by not having ?(). Some of the cow paths are now a dead-end, so you need to look at where the cows were going rather than how they got there.

When would anybody every write: foo?.bar() and want the call to throw? If that was what they wanted why not say: foo.bar() My proposal may be different from CoffeeScript, but it would seem to exactly reflect what a programmer is likely to want and my semantic sketch showed how it would work. I don't see anything confusing in my suggest semantics, I see the exact opposite.

The reason CoffeeScript offers ?( as well as ?. is precisely because, given a maybe-null-or-undefined foo.bar, you can not only further ?. your way to a baz and fail soft with undefined -- you might also want to try to invoke foo.bar as a method and fail soft if it is not invocable (typeof foo.bar != "function"). This works for an unqualified identifier foo, too:

Yes, what I'm suggesting doesn't deal with the "only invoke if a function" use case. However, I'm guessing that the "only try to invoke if it exists" use case is far more common. I'm suggesting that my semantics addresses the sweet spot of CoffeeScript combined ?. and ?() usage without having ?()

$ cat /tmp/x.coffee f = null f?() $ ./bin/coffee -p /tmp/x.coffee (function() { var f;

f = null;

if (typeof f === "function") { f(); }

}).call(this);

The basis case of this induction is an unqualified identifier, not something like foo.bar with a dot or question-dot in it. This shows that ?. is not sufficient to avoid a TypeError attempting to invoke a maybe-function. A variant such as ?( is needed after the unqualified identifier.

Right, I think that f?() is a much less interesting use case than foo?.bar?()

Alas, we can't use CoffeeScript's nice spelling, and I don't see another good way to spell it.

I still stand by my alternative semantics as being a way to address the more important use case without have ?()

I know this is kind of bikeshedding, but if it was expressed as

foo.?bar foo.?bar()

then it can be interpreted as, "take foo, and if possible, take bar" or "take foo, and if possible, call bar". Which is basically your semantics, but expressed in a way where ? is on the place of "ambiguity".

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 2:30 AM, David Bruant wrote:

What about a more generic operator that would be able to silently absorb any error?

let greedy = obj.hints?.greedy;

would become:

let greedy = silent obj.hints.greedy;

A hazard with this approach is that it could silence a broader range of errors than the programmer intended. In the above example, the programmer's intent is probably just to not try to access greedy if hints doesn't exist. However, if greedy is an accessor property that throws your silent operator would also mask that throw (while ?. would not). It isn't clear that this would be desirable if the throw is an unexpected behavior.

# Brendan Eich (13 years ago)

David Bruant wrote:

What about a more generic operator that would be able to silently absorb any error?

let greedy = obj.hints?.greedy;

would become:

let greedy = silent obj.hints.greedy;

Ignoring exact syntax, I don't think we want more general throwing suppression. The goal with existential operators is to suppress exactly the TypeError thrown when trying to get properties from null or undefined.

Suppressing errors requires care, that's why ?. is not . (the default). But even with opt-in, exceptional-circumstances syntax, we should prefer a narrower semantics to an over-broad one that will, all else equal, tend to hide bugs and fail to express precise programmer intention.

The other point I will make is that we should take the CoffeeScript user testing results to heart, moreso than thought experiments. In this light I invite all CS users to comment.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

I still stand by my alternative semantics as being a way to address the more important use case without have ?()

No, the basis cases differ and that is a bug in your alternative.

foo?.bar means take bar from foo or undefined if foo == null.

foo?.bar() means take bar from foo and throw if foo == null else try to invoke the result if typeof "function", else undefined.

This is an off-by-one bug.

# Brendan Eich (13 years ago)

Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I still stand by my alternative semantics as being a way to address the more important use case without have ?() No, the basis cases differ and that is a bug in your alternative.

foo?.bar means take bar from foo or undefined if foo == null.

foo?.bar() means take bar from foo and throw if foo == null else try to invoke the result if typeof "function", else undefined.

This is an off-by-one bug.

It also uses ?. to mean . and magically applies the ? to the later ().

Or if the semantics suppresses a TypeError both for the "take bar" and "invoke it" parts, then it's mixing two things in one under syntax located in the wrong place for the second suppression. Mainly, suppression should be precise and at the point of application -- where the dot goes if getting or setting, where the left paren goes if invoking.

Also, if you agree that the ?( case in CS is less significant, then we shouldn't add it in haste or mangle ?. semantics to try to cover it.

# Jeremy Ashkenas (13 years ago)

Sorry -- an old inbox filter caused me to miss this...

There were a couple of questions asked in the thread, but I think it might be helpful to summarize the overall rationale first.

The existential operator in CoffeeScript (?) is intended to make it easier to ask questions about whether values exist. And to do so in different circumstances where telling the difference might be useful: as a postfix, as a binary operator, as a chained accessor, etc...

The existential operator conflates null and undefined because both are the way to say "no value" in JavaScript. While it's entirely possible to define a JS api that behaves differently when you pass it null instead of undefined, doing so is almost certainly a bad idea. Long-time JavaScripters will tell you that they mean two different things: undefined is a value that has never been created whereas null is a value that has explicitly been removed -- in practice, that distinction is only ever useful for debugging ... you really don't want your code semantics to care about how a value came to not exist -- just that it does exist or doesn't.

So, given that premise, the operator allows you to ask questions about existence in the following ways:

Does the object exist? object?

Does the property exist? object.property?

Give me the object if it exists, otherwise give me the other value. object ? other

Assign the property only if it doesn't already exist. object.property ?= value

Call the method if it exists. object.method?(value)

And finally, what we're discussing here: Continue accessing into the object only if the property exists. object.property?.other.properties.go.here

I'm afraid that I don't quite follow Allen's reasoning from the comments in the previous tickets -- whether it's an object or a property is expected to exist isn't quite the point ... you want to be able to handle either case, or both at once, with equal ease.

"I was trying to illustrate a possible issue with your semantics when the existence of a property is conditional but the object that would have the property is expected to exist."

If the object may not exist: obj?.property If the property may not exist: obj.property? If both may not exist: obj?.property?

If ES.Next just does ?. and not ?(), that'll work, but will make it impossible to have method calls take part in chains of accesses.

Allen asks about when you would want to write foo?.bar()

... the only use case I can think of is when foo may or may not be defined, and you know that if it is defined, it's going to have a bar method on it, and if it is defined, you want to call bar for its side effects. A bit far fetched. The far more common case is:

options.success?(response)

... if the user passed a "success" callback, call it.

I hope all that reasoning is first-principles-y enough. For what it's worth, there is one aspect of the existential operator that I'm not satisfied with and still want to change: How the check is stricter when you're calling a function: func?().

Everywhere else in the language, ? means existence (not null or undefined) -- but when used to call a function, the check ensures that the value is callable as well. In a DWIM sense, this makes sense, because the only things you'd ever want to try to call in JavaScript must be callable ... but I think it's strange that the meaning of "existence" alters itself just for this use case. I opened a ticket talking about rolling it back to "null or undefined" semantics here:

jashkenas/coffee-script#2315

# Brendan Eich (13 years ago)

Another problem with your alternative: either it breaks a refactoring equivalence.

Let <==> be equivalence for a program fragment, and <!=> be

inequivalence. Then we have in JS today extended with do expressions (and gensym via $tmp):

foo.bar() <==> do {let $tmp = foo.bar; $tmp.call(foo)}

Now use ?. used instead of dot. Either the equivalence breaks:

foo?.bar() <!=> do {let $tmp = foo?.bar; $tmp.call(foo)}

Or else your alternative leaks Reference type values into the language, observably, because if

foo?.bar() <==> do {let $tmp = foo?.bar; ($tmp == null) ? void 0 : $tmp.call(foo)}

then

var fun = foo?.bar; fun();

should not throw if fun is null or undefined, but there's nothing in the fun() expression to signal this to the implementation or readers. This fun would have as its value a new kind of MaybeReference type. We do not want this sub-alternative.

# Brendan Eich (13 years ago)

Brendan Eich wrote:

Another problem with your alternative: either it breaks a refactoring equivalence. ... or it leaks maybe-Reference type values into the language.

# Brendan Eich (13 years ago)

Jeremy Ashkenas wrote:

Everywhere else in the language, ? means existence (not null or undefined) -- but when used to call a function, the check ensures that the value is callable as well. In a DWIM sense, this makes sense, because the only things you'd ever want to try to call in JavaScript must be callable ... but I think it's strange that the meaning of "existence" alters itself just for this use case. I opened a ticket talking about rolling it back to "null or undefined" semantics here:

jashkenas/coffee-script#2315

Apart from people misreading the proposal in this issue, it does seem to be removing a bit of utility, but perhaps that's not actually used? Do CS users try to ?(-invoke a maybe-function that is sometimes neither null nor undefined nor typeof-type "function", but rather something that coerces to object?

The way to get ?( into JS is by a longer spelling:

options.success?.(response)

Pretty ugly, but it puts the ? magic right where the maybe-test belongs.

# Domenic Denicola (13 years ago)

What struck me from Jeremy's explanation was the uniformity of CoffeeScript's ? operator (modulo the small issue he mentioned for function calls). It seems to combine the currently-strawman'ed ??/??= and ?. into one very simple semantic.

What about adopting ?? as CoffeeScript's ? operator? Would this solve the lookahead problems? So you could do

object.property??.other.properties.go.here object.method??(args)

This would unfortunately imply, for uniformity, that

object ?? other object ??= other

become null + undefined tests instead of just undefined. But that might be worth paying.

For the record that leaves object?? as the only unimplemented CoffeeScript counterpart.

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Jeremy Ashkenas wrote:

Everywhere else in the language, ? means existence (not null or undefined) -- but when used to call a function, the check ensures that the value is callable as well. In a DWIM sense, this makes sense, because the only things you'd ever want to try to call in JavaScript must be callable ... but I think it's strange that the meaning of "existence" alters itself just for this use case. I opened a ticket talking about rolling it back to "null or undefined" semantics here:

jashkenas/coffee-script#2315

Apart from people misreading the proposal in this issue, it does seem to be removing a bit of utility, but perhaps that's not actually used? Do CS users try to ?(-invoke a maybe-function that is sometimes neither null nor undefined nor typeof-type "function", but rather something that coerces to object?

The way to get ?( into JS is by a longer spelling:

options.success?.(response)

Will you include options.success?.[index] then, too?

# Brendan Eich (13 years ago)

That's possible but it is a bit ugly, isn't it?

Also, as dherman observed, ?. and variations are different from defaulting, in one point of view. That is, avoiding dereferencing a value that does not coerce to object is the purpose of ?. but not necessarily of ??= (or || as used for defaulting today).

We could use ?obj as a prefix form for testing whether obj is not null or undefined, but it would then lead to doubled ?? when put in the "then" part of a ?: ternary.

Using ?? as a suffix form is even uglier in a ternary: obj???wut:lol.

We already have obj != null for testing whether obj is not null or undefined, so that takes away a lot of the pressure for unary ??.

My current thinking is:

(a) ?? and ??= as proposed in strawman:default_operator (undefined-only). (b) ||= as a freebie since || is in the language and useful for falsy-defaulting (latent bugs notwithstanding). (c) ?. as proposed in strawman:existential_operator. (d) obj.maybeMethod?.(args) for what CoffeeScript calls ?(.

# Brendan Eich (13 years ago)

Herby Vojčík wrote:

Will you include options.success?.[index] then, too?

I wasn't planning on it. CoffeeScript does not have any such thing (spelling it ?[ would be hard in CS too, due to array literals). Could add it in the future, trying not to overdo the strawman at this point. YAGNI and KISS and when-in-doubt-leave-it-out all are against it.

# Jeremy Ashkenas (13 years ago)

On Tue, Jun 19, 2012 at 2:59 PM, Brendan Eich <brendan at mozilla.org> wrote:

Herby Vojčík wrote:

Will you include options.success?.[index] then, too?

I wasn't planning on it. CoffeeScript does not have any such thing (spelling it ?[ would be hard in CS too, due to array literals). Could add it in the future, trying not to overdo the strawman at this point. YAGNI and KISS and when-in-doubt-leave-it-out all are against it.

Actually, CoffeeScript definitely has it for consistency's sake:

coffeescript.org/#try:object.property%3F[one]

... we don't have a problem with being whitespace-sensitive for situations like this.

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 10:31 AM, Brendan Eich wrote:

Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I still stand by my alternative semantics as being a way to address the more important use case without have ?() No, the basis cases differ and that is a bug in your alternative.

foo?.bar means take bar from foo or undefined if foo == null.

foo?.bar() means take bar from foo and throw if foo == null else try to invoke the result if typeof "function", else undefined.

This is an off-by-one bug.

It also uses ?. to mean . and magically applies the ? to the later ().

Or if the semantics suppresses a TypeError both for the "take bar" and "invoke it" parts, then it's mixing two things in one under syntax located in the wrong place for the second suppression. Mainly, suppression should be precise and at the point of application -- where the dot goes if getting or setting, where the left paren goes if invoking.

I don't agree, probably because I'm look at ?. as an operator that evaluates to a special kind of Reference. Like with References in general, it is the ultimate consumer (usually via GetValue/SetValue, but not always) of the Reference that actually access the value or throws an error if appropriate.

If we say that the value Missing represents a Reference to a known to be non-existant value, then the semantics of LHS?.prop can be summarized as:

LHS ==> Result

Unresolvable Ref Missing undefined/null Missing Missing Missing other non-Ref ConditionalRef(GetValue(LHS),prop) Reference Missing or ConditionalRef(GetValue(LHS),prop) (both normal and Conditional)

This differs from LHS.prop whose corresponding table would be

LHS ==> Result

Unresolvable throw undefined/null throw Missing throw other non-Ref Reference(GetValue(LHS),prop) Reference throw or Reference(GetValue(LHS),prop) (both normal and Conditional)

In addition, GetValue(V) is extended with these cases: V ==> Result

Missing undefined ConditionalRef undefined if referenced property doesn't exist, otherwise same as normal Reference

Just like with all References, it is the consumer of a Missing/ConditionalReference value that determines what to do with. Most consumers unconditionally call GetValue however, some like typeof and delete explicitly deal with the Reference in some other manner.

All I have done is add function call to the list of operators that have some Reference sensitive semantics. It would have special handling for Missing/ConditionalRefs. I could also add "Missing" semantics to delete, but it really isn't very important there.

Also, if you agree that the ?( case in CS is less significant, then we shouldn't add it in haste or mangle ?. semantics to try to cover it.

No, I didn't say that. I said that I suspect that f?() is less significant than foo?.bar?()

I suspect that (if you could say them in JS) we would find that g(foo?.bar) and g(foo?.baz?()) are about equally significant and that people will write (or typo) foo?.bar() with the expectation that it means foo?.bar?()

The risk is in adding precisely CoffeeScript ?. in haste without thinking through implications of not also having ?(). It may mean a different variant of ?. semantics would work better in our case.

# Brendan Eich (13 years ago)

Jeremy Ashkenas wrote:

On Tue, Jun 19, 2012 at 2:59 PM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

Herby Vojčík wrote:

    Will you include options.success?.[index] then, too?


I wasn't planning on it. CoffeeScript does not have any such thing
(spelling it ?[ would be hard in CS too, due to array literals).
Could add it in the future, trying not to overdo the strawman at
this point. YAGNI and KISS and when-in-doubt-leave-it-out all are
against it.

Actually, CoffeeScript definitely has it for consistency's sake:

coffeescript.org/#try:object.property%3F[one]

... we don't have a problem with being whitespace-sensitive for situations like this.

Oops, missed that somehow. Ok, at this point my attempt to avoid ?.[ hangs only on the need for three chars.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

I don't agree, probably because I'm look at ?. as an operator that evaluates to a special kind of Reference.

That your alternative off-by-one in its basis case is bad enough -- it creates two error suppression points from one syntactic extension-point, it will hide bugs, it is gratuitously different from CoffeeScript, and it prevents using the ?( analogue on an unqualified identifier.

Worse, instead of leaking an observable ConditionalReference (whew!), you've opted to break the equivalence between foo.bar() and %tmp = foo.bar; %tmp.call(foo) for the case where . is replaced by ?. -- in this case your proposal does not throw while the expansion does.

The right extension for what CoffeeScript calls ?( is simply to use an unambiguous and backward-compatible extension, such as ?.( instead.

The risk is in adding precisely CoffeeScript ?. in haste without thinking through implications of not also having ?(). It may mean a different variant of ?. semantics would work better in our case.

I'm not adding anything in haste, and CoffeeScript has been around, what, three years. The haste here seems to be on the other foot :-|.

Also, I do not think we should extend Reference types in the spec if we can help it. These must boil away in optimizing implementations, always. Elaborating them risks leaks or more subtle dependencies happening as unintended consequences.

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 10:50 AM, Brendan Eich wrote:

Another problem with your alternative: either it breaks a refactoring equivalence.

Let <==> be equivalence for a program fragment, and <!=> be inequivalence. Then we have in JS today extended with do expressions (and gensym via $tmp):

foo.bar() <==> do {let $tmp = foo.bar; $tmp.call(foo)}

Now use ?. used instead of dot. Either the equivalence breaks:

foo?.bar() <!=> do {let $tmp = foo?.bar; $tmp.call(foo)}

Why is it important that this equivalence holds for . and ?. We already have other places using references where similar refactoring aren't equivalent:

 typeof foo   <!=> do(let $tmp = foo /*Reference error if foo unresolvable) */; typeof $tmp)

In particular, I'm pretty sure that this refactoring hazard is not going to be as significant as the foo?.bar() //oops I'm really think foo?.bar?() hazard

Or else your alternative leaks Reference type values into the language, observably, because if

foo?.bar() <==> do {let $tmp = foo?.bar; ($tmp == null) ? void 0 : $tmp.call(foo)}

then

var fun = foo?.bar; fun();

should not throw if fun is null or undefined, but there's nothing in the fun() expression to signal this to the implementation or readers. This fun would have as its value a new kind of MaybeReference type. We do not want this sub-alternative.

No, of course not. You can't allow References to leak to the user value level!

# Brendan Eich (13 years ago)

Brendan Eich wrote:

Worse, instead of leaking an observable ConditionalReference (whew!), you've opted to break the equivalence between foo.bar() and %tmp = foo.bar; %tmp.call(foo) for the case where . is replaced by ?. -- in this case your proposal does not throw while the expansion does.

The right extension for what CoffeeScript calls ?( is simply to use an unambiguous and backward-compatible extension, such as ?.( instead.

Jeremy pointed out privately that the need for ?( is less acute in practice, and also because of .call via the equivalences:

foo.bar?(args) <==> foo.bar?.call(foo, args) fun?(args) <==> fun?.call(undefined, args)

But ?.( is shorter. It's awkward to use three chars instead of CoffeeScript's two, and to have a dot in the middle where no get or set is implied.

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 12:26 PM, Brendan Eich wrote:

Brendan Eich wrote:

Worse, instead of leaking an observable ConditionalReference (whew!), you've opted to break the equivalence between foo.bar() and %tmp = foo.bar; %tmp.call(foo) for the case where . is replaced by ?. -- in this case your proposal does not throw while the expansion does.

The right extension for what CoffeeScript calls ?( is simply to use an unambiguous and backward-compatible extension, such as ?.( instead.

Jeremy pointed out privately that the need for ?( is less acute in practice, and also because of .call via the equivalences:

foo.bar?(args) <==> foo.bar?.call(foo, args) fun?(args) <==> fun?.call(undefined, args)

How are these equivalent? Won't fun?.call evaluate to undefined if fun is undefined and undefined(undefined,args) will throw...

# Brandon Benvie (13 years ago)

Is it possible to do something like

foo.bar?:(foo, args)

Basically a ternary that allows an empty component, and can tell the future (whether the other result is callable)

# Jeremy Ashkenas (13 years ago)

On Tue, Jun 19, 2012 at 3:33 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

foo.bar?(args) <==> foo.bar?.call(foo, args) fun?(args) <==> fun?.call(undefined, args)

How are these equivalent? Won't fun?.call evaluate to undefined if fun is undefined and undefined(undefined,args) will throw...

... check out the compilation:

coffeescript.org/#try:fun%3F.call(undefined%2C args) window.method%3F.call(window%2C args)

It doesn't eagerly evaluate to undefined ... the value of the entire expression is undefined if the chain is broken at the existential operator. That's much of the point of soaks:

object.property?.i.can.keep.chaining.in.here.without.throwing.errors.if.property.is.undefined ;)

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 12:37 PM, Jeremy Ashkenas wrote:

On Tue, Jun 19, 2012 at 3:33 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

foo.bar?(args) <==> foo.bar?.call(foo, args) fun?(args) <==> fun?.call(undefined, args)

How are these equivalent? Won't fun?.call evaluate to undefined if fun is undefined and undefined(undefined,args) will throw...

... check out the compilation:

coffeescript.org/#try:fun%3F.call(undefined%2C args) window.method%3F.call(window%2C args)

Ah, interesting...so this is actually close to what I was advocating for this particular case. However, if I now understand correctly you are saying that fun?.call() produces undefined if fun is null/undefined but will throw if fun is defined as: fun = new Object; because it doesn't have have "call" property.

Also, it isn't clear to me why the second example (window.method?.call(window, args)) is only guarding for null and not undefined. Is it only because you guard for undefined on variable references and not on property references?

Basically, I see what the code you generate is doing but the unlying semantics that yields that code is less obvious.

It doesn't eagerly evaluate to undefined ... the value of the entire expression is undefined if the chain is broken at the existential operator. That's much of the point of soaks:

object.property?.i.can.keep.chaining.in.here.without.throwing.errors.if.property.is.undefined ;)

Ah, again. I don't think Brendan's strawman will produce that result. The ...?.i is going to get undefined when it does GetValue

# Jeremy Ashkenas (13 years ago)

On Tue, Jun 19, 2012 at 4:35 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Ah, interesting...so this is actually close to what I was advocating for this particular case. However, if I now understand correctly you are saying that fun?.call() produces undefined if fun is null/undefined but will throw if fun is defined as: fun = new Object; because it doesn't have have "call" property.

Yes, exactly.

Also, it isn't clear to me why the second example (window.method?.call(window, args)) is only guarding for null and not undefined. Is it only because you guard for undefined on variable references and not on property references?

Ha ha, loose equality strikes again! It's actually guarding for both null and undefined. It's the only place that CoffeeScript purposefully uses the in-all-other-cases-avoided != operator. undefined == null, right?

Ah, again. I don't think Brendan's strawman will produce that result. The

...?.i is going to get undefined when it does GetValue on the Reference produced for object.property. Then undefined.can will throw in step 5 of 11.2.1 because the LHS is undefined. Getting this behavior seems to requires modifying . as well as defining ?.

I'm afraid I can't speak to the spec language or strawman ... but the basic idea with soaking is to short-circuit evaluation as soon as the soak fails. In the same way that:

window || a.b.c

... won't error even if a is entirely undeclared ...

window.a?.b.c

... won't error even if a is entirely undeclared.

# Allen Wirfs-Brock (13 years ago)

On Jun 19, 2012, at 2:15 PM, Jeremy Ashkenas wrote:

On Tue, Jun 19, 2012 at 4:35 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

...

Ha ha, loose equality strikes again! It's actually guarding for both null and undefined. It's the only place that CoffeeScript purposefully uses the in-all-other-cases-avoided != operator. undefined == null, right?

Of course! I'm blind to it because I always try to be explicit and use === tests...

Ah, again. I don't think Brendan's strawman will produce that result. The ...?.i is going to get undefined when it does GetValue on the Reference produced for object.property. Then undefined.can will throw in step 5 of 11.2.1 because the LHS is undefined. Getting this behavior seems to requires modifying . as well as defining ?.

I'm afraid I can't speak to the spec language or strawman ... but the basic idea with soaking is to short-circuit evaluation as soon as the soak fails. In the same way that:

The ES spec. is left recursive so the semantics for MemberExpression :: MemberExpression . IdentifierName

has no direct way of knowing that the MemberExpression to the left of the . contained a ?.
Similarly if that nested MemberExpression was: MemberExpression :: MemberExpression ?. IdentifierName it has no direct way (other than throwing an exception) to terminate evaluation of the MemberExpression to its right. That is why things like Reference values are used within the specification to transport semantic state around. Of course an actual implementation is not necessarily limited in this manner...

# Aymeric Vitte (13 years ago)

Coffeescript seems to have some radical behavior (a.b?.c.d.e.f) a bit similar to what I suggested (which ok can not be in js)

But the discussion here still does not say how much a.b?() or a.b?.call(xxx) is used in coffeescript

Personnaly I was thinking that ?. should more allow to check existence rather than both checking and calling it if it exists, difficult to win everywhere

Le 19/06/2012 22:35, Allen Wirfs-Brock a écrit :

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

On Jun 19, 2012, at 10:50 AM, Brendan Eich wrote:

Another problem with your alternative: either it breaks a refactoring equivalence.

Let<==> be equivalence for a program fragment, and<!=> be inequivalence. Then we have in JS today extended with do expressions (and gensym via $tmp):

foo.bar()<==> do {let $tmp = foo.bar; $tmp.call(foo)}

Now use ?. used instead of dot. Either the equivalence breaks:

foo?.bar()<!=> do {let $tmp = foo?.bar; $tmp.call(foo)}

Why is it important that this equivalence holds for . and ?.

Equivalences are not sacred but they are informative and sometimes normative (as in, a refactoring norm exists in the world).

We already have other places using references where similar refactoring aren't equivalent:

  typeof foo<!=>  do(let $tmp = foo /*Reference error if foo unresolvable) */; typeof $tmp)

That's right, and that is considered a botch not to imitate.The fact that typeof x == "function" testing is so long-winded is indeed one of the motivations for ?(.

In particular, I'm pretty sure that this refactoring hazard is not going to be as significant as the foo?.bar() //oops I'm really think foo?.bar?() hazard

CoffeeScript has years of use, and this is not something that has caused confusion (ask @jashkenas, see github issues).

# Brendan Eich (13 years ago)

Indeed I did not spec this in strawman:existential_operator#semantics -- one step at a time. Also I'm not sure how the indefinite soak will fly with others on TC39.

Allen, I agree if we do make a soak and don't spec it as CoffeeScript realizes it (by translation!) then we're looking at elaborating Reference semantics. Still smells, but it's the only plausible path if this is the goal.

More when I have time. Thanks for the feedback.

# Aymeric Vitte (13 years ago)

Complement for my last sentence : if you are using ?. it means that you don't know if the thing does exist, then you are probably waiting for it to exist (asynchronous environments for example), then once you get it, it is very unlikely that you call it right away, I would like to see some examples

Le 20/06/2012 00:36, Aymeric Vitte a écrit :

# John Tamplin (13 years ago)

On Tue, Jun 19, 2012 at 1:34 PM, Jeremy Ashkenas <jashkenas at gmail.com>wrote:

Allen asks about when you would want to write foo?.bar()

... the only use case I can think of is when foo may or may not be defined, and you know that if it is defined, it's going to have a bar method on it, and if it is defined, you want to call bar for its side effects. A bit far fetched.

It doesn't seem that far-fetched to me -- I seem to constantly write things like this in Java:

String name = (person != null) ? person.getName() : null;

IIUC, that could just be written as String name = person?.getName() if this operator were avaialble.

Maybe you are saying that you don't want to have rigid class structures in JS, but it seems reasonable that if you know person is of type Person it is supposed to have a getName method on it.

# Allen Wirfs-Brock (13 years ago)

On Jun 20, 2012, at 7:18 AM, John Tamplin wrote:

On Tue, Jun 19, 2012 at 1:34 PM, Jeremy Ashkenas <jashkenas at gmail.com> wrote: Allen asks about when you would want to write foo?.bar()

... the only use case I can think of is when foo may or may not be defined, and you know that if it is defined, it's going to have a bar method on it, and if it is defined, you want to call bar for its side effects. A bit far fetched.

It doesn't seem that far-fetched to me -- I seem to constantly write things like this in Java:

String name = (person != null) ? person.getName() : null;

IIUC, that could just be written as String name = person?.getName() if this operator were avaialble.

Maybe you are saying that you don't want to have rigid class structures in JS, but it seems reasonable that if you know person is of type Person it is supposed to have a getName method on it.

yes, but in a static Java-like language such as for you example above, the existence of person implies the existence of the getName method. The JS equivalent would likely be something like:

name = (person && person.getName) ? person.getName() : undefined;

using ?. as defined by Brendan this could be:

name = (person?.getName) ? person.getName( ) : undefined;

I've been arguing that ?. and () could be defined such that the above statement could instead be reduced to:

 name = person?.getName();

However, by Brendan's semantics this reduced form would be equivalent to:

 name = (person && person.getName) ? person.getName() : throw new TypeError;

I was hypothesizing that Brendan's semantics would seldom be the programer's intent for person?.getName() . If an exception was acceptable, why wouldn't you just say:

 name = person.getName();
# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

I was hypothesizing that Brendan's semantics would seldom be the programer's intent for person?.getName() . If an exception was acceptable, why wouldn't you just say:

Because it's rare to have a maybe-method called on a maybe-object, based on CoffeeScript experience and design. Please don't personalize this

# Allen Wirfs-Brock (13 years ago)

On Jun 20, 2012, at 10:45 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I was hypothesizing that Brendan's semantics would seldom be the programer's intent for person?.getName() . If an exception was acceptable, why wouldn't you just say:

Because it's rare to have a maybe-method called on a maybe-object, based on CoffeeScript experience and design. Please don't personalize this -- I wrote the strawman to capture what CoffeeScript has user-tested. Any bugs are mine, but they're also not (necessarily) intended.

nothing personal intended, just a sloppy way to refer to your strawman.

... Again, I do not see the point in gratuitously differing from CoffeeScript without reason. One might argue that since we cannot use ?( as CS does, we should make ?. soak up call TypeErrors. Could be, but I didn't propose anything for call yet and this discussion seems worth beating into the ground a bit more before I do :-).

yes, that's what I was proposing. I think calls need to be part of the overall discussion for this feature area.

# Brendan Eich (13 years ago)

Brendan Eich wrote:

Let's look at what CoffeeScript requires to avoid this exception:

$ cat /tmp/z.coffee person = {} person?.getName?()

$ ./bin/coffee -p /tmp/z.coffee (function() { var person;

person = {};

if (person != null) { if (typeof person.getName === "function") { person.getName(); } }

}).call(this);

$ ./bin/coffee /tmp/z.coffee && echo "no throw" no throw

But of course, to the point that "it's rare to have a maybe-method called on a maybe-object", the minimum required by CoffeeScript here is just:

$ cat /tmp/z.coffee person = {} person.getName?()

$ ./bin/coffee -p /tmp/z.coffee (function() { var person;

person = {};

if (typeof person.getName === "function") { person.getName(); }

}).call(this);

$ ./bin/coffee /tmp/z.coffee && echo "no throw" no throw

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

yes, that's what I was proposing. I think calls need to be part of the overall discussion for this feature area.

Jeremy is kindly grepping more codebases, but just from CoffeeScript (self-hosted) source itself, ?. is 42, ?[ is 7, and ?( is 3 -- so 12:2:1. Supports deferring ?( for now given syntax issue, but the counter-argument is that we might solve syntax holistically a different way. One idea:

foo.?bar instead of foo?.bar

which enables

foo(? args )

instead of

foo?( args )

and implies

foo[? index ]

This is infelicitous compared to putting ? first because it separates the ? from the maybe-nully left operand too much, and looks ugly (to Spanish readers it looks even more wrong).

The other idea mooted:

foo?:( args )

runs into the X ?: Y problem -- that should mean X ? X : Y per GNU C and intuition, but the : in the middle of ?:( doesn't mean any such thing.

My super-flammable straw syntax of foo?.(args) has a similarly inapt dot in the middle.

Suggestions welcome. If we must get maybe-call in or at least future-proof the syntax for it a bit, now is the time.

# Brendan Eich (13 years ago)

Brendan Eich wrote:

This is infelicitous compared to putting ? first because it separates the ? from the maybe-nully left operand too much, and looks ugly (to Spanish readers it looks even more wrong).

It's also a loss to deviate from CS, but we can't do ?( so we're half (or 1/14th by the CoffeeScript usage-grep stats) deviated.

And putting ? after ( is future-hostile to any ?-unary-prefix operator, but I believe the only viable such prospective syntax is in patterns (destructuring on steroids), so not after a ( that starts an argument list.

# John Tamplin (13 years ago)

On Wed, Jun 20, 2012 at 12:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

yes, but in a static Java-like language such as for you example above, the existence of person implies the existence of the getName method. The JS equivalent would likely be something like:

Is it really rare to write JS code that assumes objects passed to it have an expected structure? Obviously, you don't have to, but when I have seen object-oriented code written in JS it tends to make such assumptions.

If I write something that takes either null or a person object, I assume if it isn't null it really is a person object and I don't write checks to verify it. If someone passes something that doesn't have getName on it to my method, I am perfectly fine with it blowing up. When I expect that it might be null/undefined, then I have to write a bunch of boilerplate to deal with that, and ?. allows removing that boilerplate.

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Allen Wirfs-Brock wrote:

yes, that's what I was proposing. I think calls need to be part of the overall discussion for this feature area.

Jeremy is kindly grepping more codebases, but just from CoffeeScript (self-hosted) source itself, ?. is 42, ?[ is 7, and ?( is 3 -- so 12:2:1. Supports deferring ?( for now given syntax issue, but the counter-argument is that we might solve syntax holistically a different way. One idea:

foo.?bar instead of foo?.bar

which enables

foo(? args )

instead of

foo?( args )

and implies

foo[? index ]

This is infelicitous compared to putting ? first because it separates the ? from the maybe-nully left operand too much, and looks ugly (to Spanish readers it looks even more wrong).

Let's allow foo.? to denote "soft foo", so you get:

foo.?.bar // foo == null ? undefined : foo.bar foo.?(bar) // foo == null ? undefined : foo(bar) foo.?[bar] // foo == null ? undefined : foo[bar]

but maybe also more esoteric uses, like:

for (let key in foo.?) // for (let key in (foo == null ? {} : foo)) foo.? // foo == null ? undefined : foo // sort-of clearing foo.? = bar // foo == null ? undefined : (foo = bar) // this semantics is logical, just don't know // what would be the use case... foo.?.baz = bar // foo == null ? undefined : (foo.baz = bar) // this is usable ... foo.? ...

# Brendan Eich (13 years ago)

Herby Vojčík wrote:

Let's allow foo.? to denote "soft foo", so you get:

foo.?.bar // foo == null ? undefined : foo.bar foo.?(bar) // foo == null ? undefined : foo(bar) foo.?[bar] // foo == null ? undefined : foo[bar]

?. works as well for these and the main (by use-frequency, so far) form matches CS. If we must have extra dots in the lesser-used forms, I'd put the dot in the middle to keep the question-mark on the left.

but maybe also more esoteric uses, like:

for (let key in foo.?) // for (let key in (foo == null ? {} : foo))

That is even more of an abuse of dot, and it does not play well with ?: ternaries and the proposed ?? operator.

foo.??bar:baz

would be legal by maximal munch, parsed as

(foo.?)?bar:baz.

Beyond this objection, the need for foo.? instead of foo != null is not strong.

Worse, you show magic defaulting to {} in the for-in case, but the default result for anything like a suffix-? expression in CS is a boolean boolean result:

$ cat /tmp/w.coffee lhs = foo?

$ ./bin/coffee -p /tmp/w.coffee (function() { var lhs;

lhs = typeof foo !== "undefined" && foo !== null;

}).call(this);

foo.? // foo == null ? undefined : foo // sort-of clearing

See above.

foo.? = bar // foo == null ? undefined : (foo = bar) // this semantics is logical, just don't know // what would be the use case... foo.?.baz = bar // foo == null ? undefined : (foo.baz = bar) // this is usable

These don't win over ?. in the proposal.

# Aymeric Vitte (13 years ago)

Because it's rare to have a maybe-method called on a maybe-object,

Yes it's probably rare as I previously mentioned too, thinking about it I still can not find a single example of use, then maybe CS users can highlight this

Existence looks more important than callable, the CS's way like a?.b.c.d.e is interesting

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

Let's allow foo.? to denote "soft foo", so you get:

foo.?.bar // foo == null ? undefined : foo.bar foo.?(bar) // foo == null ? undefined : foo(bar) foo.?[bar] // foo == null ? undefined : foo[bar]

?. works as well for these and the main (by use-frequency, so far) form matches CS. If we must have extra dots in the lesser-used forms, I'd put the dot in the middle to keep the question-mark on the left.

but maybe also more esoteric uses, like:

for (let key in foo.?) // for (let key in (foo == null ? {} : foo))

That is even more of an abuse of dot, and it does not play well with ?: ternaries and the proposed ?? operator.

This is usable case. It is not nice to test whether I can use for-in.

foo.??bar:baz

would be legal by maximal munch, parsed as

(foo.?)?bar:baz.

C'est la vie. :-) Well, this is something that can be though of, later. I would not put it away because of this edge case (which is just strange, but not incorrect).

Beyond this objection, the need for foo.? instead of foo != null is not strong.

Worse, you show magic defaulting to {} in the for-in case, but the

Well, I though of Object.keys(foo.?) and saw the {} case (or something similar) is in fact a very good case. It can be used for foo.? uniformly (I'll denote the object [[NPO]] as the short for [[NullPatternObject]]:

foo.?.bar // (foo == null ? [[NPO]] : foo).bar // [[NPO]] returns [[NPO]] from [[Get]] foo.?(bar) // (foo == null ? [[NPO]] : foo)(bar) // [[NPO]] does nothing when [[Call]], // returns [[NPO]] foo.?[bar] // (foo == null ? [[NPO]] : foo)[bar] // [[NPO]] returns [[NPO]] from [[Get]] for (let key in foo.?) // for (let key in (foo == null ? [[NPO]] : foo)) // [[NPO]] enumerates no keys Object.keys(foo.?) // Object.keys(foo == null ? [[NPO]] : foo) // [[NPO]] has empty key list foo.? // foo == null ? [[NPO]] : foo // [[NPO]] if false if ToBoolean, == null, // == undefined, === [[NPO]], !== undefined, // !== null foo.?.baz = bar // (foo == null ? [[NPO]] : foo).baz = bar // [[NPO]] does nothing when [[Put]]

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently.

# Brendan Eich (13 years ago)

Herby Vojčík wrote:

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently.

No, because (a) the overhead of a new object is too high; (b) with any kind of suffix-? or suffix-.? as you proposed it would be observable that you get a new object instead of short-circuiting to undefined

# Jeremy Ashkenas (13 years ago)

As requested, running those rough greps again on the top ten most popular CoffeeScript repos on Github yields:

188 uses of object?.property

32 uses of object?[key]

50 uses of function?(args)

46 uses of variable ?= value

... and the other ones are a bit tricker to grep for. There are at least a handful more uses of the function?(args) style but with implicit parentheses: function? arg1, arg2 ...

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently.

No, because (a) the overhead of a new object is too high; (b) with any kind of suffix-? or suffix-.? as you proposed it would be observable that you get a new object instead of short-circuiting to undefined -- the new object is exposed in the language.

What's wrong with it per se? Let it be exposed, let people use it. Some of uses will be wrong, they will eventually die, some of them will be fine, they survive (no need to add keyword or API for it, null.? yields it and it is usably short).

And BTW, if foo.? is too long and abuse of dot, you can use for example postfix tilde to get foo~.bar, foo.bar~(), "bar" in foo~ etc.

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently.

No, because (a) the overhead of a new object is too high; (b) with any

Spec / impl overhead or memory / perf overhead? Because the latter is of little worries, common uses of foo.? like foo.?.bar can be of course shortcut without using [[NullPatternObject]] at all.

# Domenic Denicola (13 years ago)

On Jun 21, 2012, at 3:22, "Herby Vojčík" <herby at mailbox.sk> wrote:

Brendan Eich wrote:

Herby Vojčík wrote:

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently.

No, because (a) the overhead of a new object is too high; (b) with any kind of suffix-? or suffix-.? as you proposed it would be observable that you get a new object instead of short-circuiting to undefined -- the new object is exposed in the language.

What's wrong with it per se? Let it be exposed, let people use it. Some of uses will be wrong, they will eventually die, some of them will be fine, they survive (no need to add keyword or API for it, null.? yields it and it is usably short).

And BTW, if foo.? is too long and abuse of dot, you can use for example postfix tilde to get foo~.bar, foo.bar~(), "bar" in foo~ etc.

/be

Herby

Language-level support for the null object pattern would be pretty excellent! I think, given the CoffeeScript grep stats (not to mention common options = options || {} code), people are definitely using ? in that capacity. The possibility of introducing something elegant like this seems like exactly why getting in only property-access ?. would be a mistake.

# Brendan Eich (13 years ago)

Replying to both Herby and Domenic here.

Domenic Denicola wrote:

On Jun 21, 2012, at 3:22, "Herby Vojčík"<herby at mailbox.sk> wrote:

Brendan Eich wrote:

Herby Vojčík wrote:

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently. No, because (a) the overhead of a new object is too high; (b) with any kind of suffix-? or suffix-.? as you proposed it would be observable that you get a new object instead of short-circuiting to undefined -- the new object is exposed in the language. What's wrong with it per se?

  1. The overhead, which is not wanted in almost all cases. Then saying "implementations may optimize" simply makes a de-facto standard with high implementation cost inevitable. We should normatively spec what CoffeeScript does -- boolean result for suffix-? or anything like it
# John Tamplin (13 years ago)

So do you have to do it by treating the ?. operator as a standalone? Instead, could you do the "indefinte soak" part during parsing, treating the rest of the expression differently after having seen a ?. operator?

# Brendan Eich (13 years ago)

John Tamplin wrote:

So do you have to do it by treating the ?. operator as a standalone?

Not sure what you mean here -- "standalone ?" (with an English-langauge ? implied after in your sentence? ;-)?

Or something else that amounts to a concealed Reference or Nil-value-proxy expression?

Just talking spec here: as Allen mentioned, ECMA-262 specifies semantics by evaluating productions in a mostly-LR(1) grammar, so member and call expressions (left-associative) have to result in some kind of (spec-internal or language-external) value.

Thus foo?.bar.baz.quux is really (((foo?.bar).baz).quux).

Instead, could you do the "indefinte soak" part during parsing, treating the rest of the expression differently after having seen a ?. operator?

Indeed one can translate when parsing. CoffeeScript does this, with some separate passes for its other purposes (implicitly declared variables, indentation-based block structure, etc.).

The ES specs can't do this, though, not without a total rewrite.

# Brendan Eich (13 years ago)

Brendan Eich wrote:

Thus foo?.bar.baz.quux is really (((foo?.bar).baz).quux).

So (as many have noted) parenthesization does not call GetValue in ECMA-262:

11.1.6 The Grouping Operator

The production PrimaryExpression : ( Expression ) is evaluated as follows:

  1. Return the result of evaluating Expression. This may be of type Reference.

NOTE This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions

# John Tamplin (13 years ago)

On Thu, Jun 21, 2012 at 11:23 AM, Brendan Eich <brendan at mozilla.org> wrote:

John Tamplin wrote:

So do you have to do it by treating the ?. operator as a standalone?

Not sure what you mean here -- "standalone ?" (with an English-langauge ? implied after in your sentence? ;-)?

Or something else that amounts to a concealed Reference or Nil-value-proxy expression?

Just talking spec here: as Allen mentioned, ECMA-262 specifies semantics by evaluating productions in a mostly-LR(1) grammar, so member and call expressions (left-associative) have to result in some kind of (spec-internal or language-external) value.

Thus foo?.bar.baz.quux is really (((foo?.bar).baz).quux).

Yes, and I am suggesting during parsing it gets treated as:

(((foo?.bar)?.baz)?.quux)

because we saw a ?. operator earlier in the expression. That way you don't have to get the Coffeescript behavior by introducing some fake object or a MaybeReference, and you just implement the ?. operator as always returning the LHS if it is null/undef.

Indeed one can translate when parsing. CoffeeScript does this, with some separate passes for its other purposes (implicitly declared variables, indentation-based block structure, etc.).

The ES specs can't do this, though, not without a total rewrite.

I realize it would be changes, but then so would adding a MaybeReference. Just thought I would bring it up.

# Brendan Eich (13 years ago)

John Tamplin wrote:

On Thu, Jun 21, 2012 at 11:23 AM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

John Tamplin wrote:

    So do you have to do it by treating the ?. operator as a
    standalone?

Not sure what you mean here -- "standalone ?" (with an
English-langauge ? implied after in your sentence? ;-)?

Or something else that amounts to a concealed Reference or
Nil-value-proxy expression?

Just talking spec here: as Allen mentioned, ECMA-262 specifies
semantics by evaluating productions in a mostly-LR(1) grammar, so
member and call expressions (left-associative) have to result in
some kind of (spec-internal or language-external) value.

Thus foo?.bar.baz.quux is really (((foo?.bar).baz).quux).

Yes, and I am suggesting during parsing it gets treated as:

(((foo?.bar)?.baz)?.quux)

because we saw a ?. operator earlier in the expression.

Ok, but this doesn't seem observable, or necessary in the spec.

That way you don't have to get the Coffeescript behavior by introducing some fake object or a MaybeReference,

CoffeeScript does not introduce a fake object or MaybeReference:

$ cat /tmp/soak.coffee o = null console.log(o?.p.q.r.s.t)

$ ./bin/coffee -p !$ ./bin/coffee -p /tmp/soak.coffee (function() { var o;

o = null;

console.log(o != null ? o.p.q.r.s.t : void 0);

}).call(this);

Rather, ECMA-262 would need to elaborate its internal Reference type as Allen wrote.

and you just implement the ?. operator as always returning the LHS if it is null/undef.

Note also that CoffeeScript does not short-circuit to the unbound/null/undefined LHS "value", it always uses void 0 -- AKA undefined.

Indeed one can translate when parsing. CoffeeScript does this,
with some separate passes for its other purposes (implicitly
declared variables, indentation-based block structure, etc.).

The ES specs can't do this, though, not without a total rewrite.

I realize it would be changes, but then so would adding a MaybeReference. Just thought I would bring it up.

Allen's right, MaybeReference is a much (much!) smaller change, especially if we want to support the ?( and suffix-? variants.

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Replying to both Herby and Domenic here.

Domenic Denicola wrote:

On Jun 21, 2012, at 3:22, "Herby Vojčík"<herby at mailbox.sk> wrote:

Brendan Eich wrote:

Herby Vojčík wrote:

I feel there is objection to introduce magical [[NullPatternObject]] into language, but all of CS-style soft-accesses could be solved very cleanly and consistently. No, because (a) the overhead of a new object is too high; (b) with any kind of suffix-? or suffix-.? as you proposed it would be observable that you get a new object instead of short-circuiting to undefined -- the new object is exposed in the language. What's wrong with it per se?

  1. The overhead, which is not wanted in almost all cases. Then saying "implementations may optimize" simply makes a de-facto standard with high implementation cost inevitable. We should normatively spec what CoffeeScript does -- boolean result for suffix-? or anything like it -- or else add a singleton "Nil" object that can be reused.

  2. It's not what CoffeeScript does.

  3. For foo?.bar if foo is not bound or has null or undefined value, if I understand correctly, you propose to use ({}) instead. This does not provide "indefinite soak", where foo?.bar.baz.quux silently short-circuits to undefined value on unbound or ==-null foo. And if Object.prototype.bar exists, foo?.bar where foo is unbound/==-null will wrongly evaluate to Object.prototype.bar's value.

  4. Likewise foo?(bar) or however it might be spelled will try to invoke an empty fresh object, throwing a TypeError -- not what we want.

  5. if (foo.?) as Herby proposed evaluates to a truthy fresh object if foo is unbound or has ==-null value. CoffeeScript and any sane interpretation of suffix-? or variant spelling wants falsy if not false result for unbound/==-null foo.

These are problems per se. But see below on the Nil singleton idea.

Let it be exposed, let people use it. Some of uses will be wrong, they will eventually die, some of them will be fine, they survive (no need to add keyword or API for it, null.? yields it and it is usably short).

And BTW, if foo.? is too long and abuse of dot, you can use for example postfix tilde to get foo~.bar, foo.bar~(), "bar" in foo~ etc.

/be Herby

Language-level support for the null object pattern would be pretty excellent!

Depends on what you mean by "null object".

I don't believe anyone wants a fresh empty object for each ? ?. ?( or however we spell the variants. There's no evidence for this from CoffeeScript. A fresh object as default value breaks indefinite soak and may expose proto-properties (3), breaks the ?( call variant (4), and breaks boolean tests of suffix-? expressions (5), as far as I can tell.

I think, given the CoffeeScript grep stats (not to mention common options = options || {} code), people are definitely using ? in that capacity.

Sure, no one argues otherwise. The minority use-cases could wait, but we should discuss them before deferring. Agree with all that. But a "Null object" meaning fresh empty object is no solution, per (1-5) above.

The possibility of introducing something elegant like this seems like exactly why getting in only property-access ?. would be a mistake.

What is elegant may be something you haven't actually described: a "Nil singleton", a proxy actually, which can soak up indefinite gets, sets, and calls. It must be a singleton to avoid the overhead problem.

Adn that is precisely what I proposed under name [[NullPatternObject]]. So all of (1), (3), (4) and (5) are void.

This Nil proxy would take the place of the concealed Reference type that Allen suggested. It has some appeal to fans of Smalltalk who message nil.

Smalltalk nil is a bit different from Null Pattern. I proposed Null pattern (optimized off when not actually needed in the equation, but exposed when needed).

Such a Nil proxy still cannot masquerade as false, though, and that is required for suffix-? testing. An object that can convert to false awaits value objects (strawman:value_objects) married with proxies to make value proxies.

Such a [[NullPattternObject]] can ToBoolean to false. No problem I see.

# Brendan Eich (13 years ago)

Herby Vojčík wrote:

Adn that is precisely what I proposed under name [[NullPatternObject]]. So all of (1), (3), (4) and (5) are void.

Apologies, I missed that and saw only someone (perhaps it was Domenic) write ({}) as the default value, which is not the same.

Such a Nil proxy still cannot masquerade as false, though, and that is required for suffix-? testing. An object that can convert to false awaits value objects (strawman:value_objects) married with proxies to make value proxies.

Such a [[NullPattternObject]] can ToBoolean to false. No problem I see.

You're right we could hardcode a special case in ToBoolean ahead of value objects.

To prevent this object from escaping into the language, we would need a special case in GetValue too. That makes this equvalent to Allen's conditional Reference (MaybeReference). Or were you thinking of letting this magic singleton esape into the language?

If so, I think you'll get serious push-back on TC39. Escaping raises the ante quite a bit. We have to be sure a falsy object won't confuse some code (SES code in particular) in a way that could lead to an exploit.

Apart from SES-focused concerns, letting the magic reference-like type escape exposes something ahead of schedule, before value objects in particular. If there's no need to let this thing escape, then we sholudn't. And in that case it's equivalent to MaybeReference.

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

Adn that is precisely what I proposed under name [[NullPatternObject]]. So all of (1), (3), (4) and (5) are void.

Apologies, I missed that and saw only someone (perhaps it was Domenic) write ({}) as the default value, which is not the same.

Such a Nil proxy still cannot masquerade as false, though, and that is required for suffix-? testing. An object that can convert to false awaits value objects (strawman:value_objects) married with proxies to make value proxies.

Such a [[NullPattternObject]] can ToBoolean to false. No problem I see.

You're right we could hardcode a special case in ToBoolean ahead of value objects.

To prevent this object from escaping into the language, we would need a special case in GetValue too. That makes this equvalent to Allen's conditional Reference (MaybeReference). Or were you thinking of letting this magic singleton esape into the language?

Definitely. It brings all pros of Null Pattern there (all of the uses I included in previous posts, automatically and consistently).

When it is explicit, users can begin to use it willingly. It may bring previously hard-to-get solutions.

If so, I think you'll get serious push-back on TC39. Escaping raises the ante quite a bit. We have to be sure a falsy object won't confuse some code (SES code in particular) in a way that could lead to an exploit.

That I cannot envision... but Null Pattern object that produces itself for all operations ([[Get]], [[Call]], ...) should not be problematic.

# Brendan Eich (13 years ago)

Herby Vojčík wrote:

That I cannot envision... but Null Pattern object that produces itself for all operations ([[Get]], [[Call]], ...) should not be problematic.

You might be surprised (I am) by how seemingly innocent things can become problematic.

Just on aesthetic grounds, I bet TC39ers will react to this the way we react to document.all that masquerades as undefined.

BTW, "Pattern" and "Null" are both not good words to join to name this thing. A pattern matching strawman exists, wherein patterns are special forms, built from destructuring patterns, used in certain syntactic forms but not first-class objects. And Null is to close to null and the ECMA-262 internal Null type.

As a Unix hacker I can dig the /dev/null reference, if there is one, but it's too far afield.

I do think Smalltalk's nil, even though not identical, suggests a better name. If we were to expose this singleton, we could do worse than call it something "the Nil object". But I'm not sold on exposing it.

Allen (and Mark if he has time) should weigh in.

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

That I cannot envision... but Null Pattern object that produces itself for all operations ([[Get]], [[Call]], ...) should not be problematic.

You might be surprised (I am) by how seemingly innocent things can become problematic.

Just on aesthetic grounds, I bet TC39ers will react to this the way we react to document.all that masquerades as undefined.

BTW, "Pattern" and "Null" are both not good words to join to name this

I just named it on the grounds of the (non-GoF) design pattern which is called "Null Object" or "Null Pattern", if I am not mistaken. But of course naming is not that important.

# Aymeric Vitte (13 years ago)

I guess the grep work is not easy to do, but I have tried (manually) to find ?. or ?( in coffee script projects, trying to seek into projects of main contributors.

Maybe not lucky or not really aware of the most popular projects, but I did not find any up to now.

According to my previous posts I am quite convinced it has an interest, I remain perplex regarding the use of ?( , then, I am really curious to see CS's uses, could you please highlight some projects ?

Le 21/06/2012 05:54, Jeremy Ashkenas a écrit :

# Jeremy Ashkenas (13 years ago)

On Thu, Jun 21, 2012 at 5:55 PM, Aymeric Vitte <vitteaymeric at gmail.com>wrote:

According to my previous posts I am quite convinced it has an interest, I remain perplex regarding the use of ?( , then, I am really curious to see CS's uses, could you please highlight some projects ?

Certainly. Here's a brief sampling of some of the uses.

Mostly cases where function values may not exist, or cases where you're passing a very heterogenous set of objects into an API, and you're not entirely sure what methods are available, but want to look for specific things.

if typeof block is 'function' then block.call(@, params) else @[block]?(params)

hashKeyFor: (obj) -> obj?.hashKey?() or obj

callback?(err, mappedRecords, env)

unless varBase.hasProperties?()

callback?()

return true if lhs?.isEqual?(rhs) and rhs?.isEqual?(lhs)

wrapper = wrapper?(core) or wrapper