ID:2240184
 
Resolved
Two new operators, ?. and ?:, have been added. These are null-conditional operators. When reading a?.b, the result will be null and will not crash the proc if a is null. When writing to a?.b, the write will simply not happen if a is null.
Applies to:DM Language
Status: Resolved (512.1386)

This issue has been resolved.
Syntactic sugar access operators.
From C#: https://msdn.microsoft.com/en-us/library/dn986595.aspx

The null-conditional operator, .? (and ?[] for lists), checks if a reference exists before trying to access the member.
// Syntax:

a?.b
a?[b]

// Of course, it can be chained
a?.b?.c?.d

// and used with procs
A()?.B()?.C()?.D()

// Examples:

var x = whatever?.variable
var y = whatevers?[index]
Whatever()?.Procedure()

// Current Equivalents:

// var whatever/whatever; /whatever has a "variable" var.
var x = whatever && whatever.variable

// Works as expected for list indexing.
// var list/whatevers
var y = whatevers && whatevers[index]

// The left-hand expression of ?. is only evaluated once.
// When typed procs are added, this should be typed.
var result = Whatever()
result && result:Procedure()
yes plz
Especially if if throws an exception on failure that you can catch or ignore without it crashing out.
^
In response to Nadrew
I don't think it should throw an exception. The operation should just short-circuit on the leftmost false-ish value, as the equivalents do, and as is done in C#. If you want to handle an exception, then you should just not check for a null reference in the first place.
Yeah, I guess just tossing the results into an if() would work just as well as letting it exception out for try/catch. As long as it's not a pain in the butt to catch failure I don't care how it works =P
Yeah, I've been thinking about this since implementing the chain . operator and I think it would be fairly doable. It would be a simple matter of converting the process to put it under a node that acted like the && operator.

The only difficulty inherent here is that, when not relying on the new chains that have to lose type specificity, I would want DM to still throw errors on trying to access a var that doesn't exist. That is, I want separate ?. and ?: operators. However the way the parser handles the . chain operator would make this rather difficult, because the var path isn't evaluated for validity until a later step. So what I might have to do for this to work is to create two different var paths, giving the operator node three children, and I'll probably also have to change the dot-to-colon conversion I added to the . operator. Still, all of these are approachable problems.
I had to do some modification of the . chain code to make this work, but the good news is not only does it work, but I managed to improve on some of the chain code in the process. Although I have not yet added a conditional list index operator (and a tricky question there is, should I check to see if the list is an actual list or just do a true/false check like the others), I have managed to add the ?. and ?: operators.

The ?. operator actually handles type checking properly; after a proc call or list index however it behaves like the new . chain operator does and ignores type, since there's no type info available.

Even better, ?. and ?: can be used as LHS values.

world << a?.b
a?.b = x

On the RHS this functions much like a && operator; it uses a slightly different layout because of the fact that unlike && it needs to retain the value if it's true. On the LHS it's a little more like an if() block. Both of these would probably be a lot cleaner with some new instruction codes, so I may decide to do that.

Useful properties of the ?. and ?: operators:

- The left side of the operator is only evaluated once; if it's a proc call that means you don't have to store the result in a temporary var.
- When using ?. or ?: for an LHS expression, the RHS is always evalulated. In other words, client.?myvar = MyProc() always runs MyProc() even if the assignment can't be made.

As currently implemented, these operators use a strict true/false test, so technically values 0 and "" would fail as if they were null. In some ways I'd prefer them to use more of an !isnull() format, which would definitely require new instruction codes, but on the other hand the basic true/false test is really fine.
You know, I'm thinking I need to change this. The ?. And ?: combinations can come up under other conditions, and ?. is especially a problem because of cases like a?.:0 or such. IIRC you can't use .: directly but the ?. could potentially confuse old code. Then again, .? and :? might be worse.
:? is right out since that would overlap with var?"success":"failure".
[expression]?[expression]:[expression]

[expression]:?[label]

No overlap.
Yeah, :? is fine; and really so is ?: as well. It's the fact that ?. and .? are both a little iffy that's an issue.

With .? there is one advantage: With .?a:b it's clear that there's no LHS to .?, and therefore . must be an expression and ? an operator. However, the way this is parsed makes it difficult to sort the two cases out separately.

Maybe I should stick with ?. and ?: after all. The odds of existing code using an expression like a?.:b are slim to none, even in a 4K. I didn't use it in Runt or SotS, and if I didn't use it there's a good chance no one did.
Compiled versions of those games wouldn't be effected anyways, so I don't see it being a problem.
I mentioned this in today's development news, but just to make it official I stuck with ?. and ?: as the operators.

Still haven't made up my mind about ?[] but I'll keep thinking about it. It seems to me that a bad index is more likely than a bad list, and a bad index wouldn't be caught. But then again this would fit well with my preferred slot-based equipment system.
Lummox JR resolved issue with message:
Two new operators, ?. and ?:, have been added. These are null-conditional operators. When reading a?.b, the result will be null and will not crash the proc if a is null. When writing to a?.b, the write will simply not happen if a is null.