Groovy question

I'm busy changing the code for my 7 Fibaro FGR-223 devices.

Question: Coming from C++ I can follow along for the most part with the Groovy code but what does ?: mean in the following line:

	if (value >= (openOffset ?: 95)) {

Reason for changing the code:

I want to change the way on/up/open/close etc is defined

In The Netherlands we have what we call "Markiezen". don't know the translation for this in English

image

I want to see in the dashboard UP (0%) or DOWN (100%)
So I'm tinkering with the groovy code of the existing FGR-223 driver which is fun!

It's got a nickname of the Elvis Operator and is a shortening of the ternary operator.

In your example it would detect an 'empty' openOffset and give it a value of 95. If openOffset had a value, it goes untouched. In other words, it's a way to apply a preset to 'value'. That 'openOffset' is being tested for "true" -- groovy true.

"Markiezen" = "awnings"

3 Likes

Close, but not quite. If openOffset is non-zero non-null, the comparison would be to openOffset, otherwise to 95. What you show is shorthand for this:

if (value >= (openOffset ? openOffset : 95)) {

Effectively it's treating openOffset ? as a Boolean value test, where 0 or null return false, and all other values return true. What is left of the colon is the true case and right is the false case.

Often code will fail with this because of not anticipating zero as a valid value. If one only wants 95 if openOffset is null, then that would have to be tested for explicitly, like this:

if (value >= (openOffset != null ? openOffset : 95)) {

or flipped around

if (value >= (openOffset == null ? 95 : openOffset)) {

5 Likes

@csteele @bravenel Thanks you so much! I know the Ternary operator from C++ but didn't manage to recognise it in the way it was written in Groovy. Duh!

Awnings... great!, could not find the translation

exactly, "Groovy Truth"

2 Likes

The Ternary operator exists also in Groovy:
x = condition ? valueCaseTrue : valueCaseFalse

Groovy only knows the additionally special case
x = value == null ? valueCaseNull : value
which can be shortened to:
x = value ?: valueCaseNull

1 Like

Not quite, though....see above for "Groovy truth" and why the second isn't exactly the same. :slight_smile:

1 Like

Here's probably my favorite example:

String label = device.label ?: device.name

Screen Shot 2022-10-29 at 10.11.57 AM

Where the string variable "label" gets one of the two names we give our devices.. either the Device Label if there's something in there or Device Name if not..

"label" of course gets used a line or two later to identify the device in a debug or Event description field.

Here's a way more interesting example:

if (enableDebug) log.info "Received command from client: [\"${device.label ?: device.name}\": ${params.deviceCommand}]"

This would result in a debug log message of: Received command from client: ["MultiSensor6C": on]
assuming the above device name/label.

You're right, of course, but can I just vent a bit that IMHO it would be so much better if the Elvis operator semantics (or maybe Groovy truth in general?) didn't evaluate a 0 value as false for numeric types.

It makes for different structure of logic expressions for conditionals between numerics and every other type, and in my experience often makes code more complicated that could otherwise have be very compact due to the elegant concept of the Elvis operator.

I know if I'm checking for and handing a zero value rather than just a null check, and I usually do it explicitly with a conditional or as a switch case if there is a range of values. Having the result being ambiguously either 0 or null almost encourages the type of bugs that @bravenel described.

Absolutely necessary that it does! But, Groovy is what it is, right?

Only if you are unaware. I only pointed it out because I know I've stepped in it from time to time.

1 Like

I've seen this a lot (maybe in ported ST code?) and wonder why the dev didn't just use device.displayName instead, which is probably exactly that implementation. :smiley:

Yeah, there are times when it is handy, then there are times when it makes it easy to introduce unintended bugs. It's not just numeric types, either--empty strings, empty lists, empty maps, etc., will all evaluate as false.

1 Like

Trust me, I'm aware but still have to re-learn the hard way every once in a while. Most people are humans and make mistakes.

It's not just numeric types, either--empty strings, empty lists, empty maps, etc., will all evaluate as false

Great reminder, thanks @bertabcd1234

Forgot to acknowledge that this is totally true. The best you can do is take advantage of its strengths and code defensively against its potential pitfalls. :wink:

1 Like

One coder's pitfall is another coder's power. Having coded in Groovy daily for several years, I can attest to the power part, and wouldn't want to change Groovy truth. I continue to be amazed at the depth lurking in Groovy, and I'm sure I have not yet plumbed all of those depths. My code today does not resemble the way it was years ago. Elvis operator is in constant use, but only rarely does the 0 being same as null issue arise. It allows 0 to be used intentionally more than it creates the ambiguity. As an old CS professor told us, there are only 3 numbers that matter: 0, 1, and any other number.

3 Likes

I was taught the same thing. Then I learned the additional enormous value of null checks when writing object oriented code, which changes the game when new innovation brings about an interesting operator like the Elvis operator in Groovy.

Anyway, I think we're at a point of "agreeing to agree" on this topic. 8)

3 Likes

That's really the (same) bytecode generated. :wink:

Probably you mean, when x is of type boolean that it will casted to true/false. But that's not a functionality of the Elvis Operator.

Veto! :wink:
I love Groovy exactly for all those short cuts! :heart_eyes:

When you really have to know if there is a null value, than just check exactly therefore, e.g.
If (x == null) x = 'undefined' // or what you like to do...

I might be misunderstanding you, but that's not what I meant; my point is that the two lines are not (necessarily) equivalent in outcome. So I meant that if you expand your example into a full script and use a value for value that is not "Groovy true," like a zero, an empty string, empty list or map, or false, then you'll get different output in both cases:

def value = 0
def valueCaseNull = "surprise!"

x = value == null ? valueCaseNull : value
println x  // prints 0

x = value ?: valueCaseNull
println x  // prints "surprise!"

The only case they are equivalent is when the value is, in fact, null, and the issue I was trying to point out is that this confuses new-to-Groovy programmers sometimes since this is different from, say, Java. :slight_smile: This is the point of confusion a couple of us addressed above.

(For those curious who don't have a Groovy interpreter handy, run it through something like https://groovyconsole.appspot.com and see the results.)

4 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.