Jefferson H created GROOVY-12442:
------------------------------------
Summary: Introduce a null-coalescing operator (??) that provides a
default value only when the left-hand operand is null, independently of Groovy
Truth.
Key: GROOVY-12442
URL: https://issues.apache.org/jira/browse/GROOVY-12442
Project: Groovy
Issue Type: Wish
Components: syntax
Reporter: Jefferson H
h2. Motivation
Groovy currently provides the Elvis operator ({{{}?:{}}}) for default values.
However, its behavior relies on Groovy Truth, which treats values such as
{{{}0{}}}, {{{}false{}}}, empty strings, and empty collections as false.
This behavior is useful in many situations, but it can also lead to unexpected
results when those values are intentional and valid.
*For example:*
{code:java}
def retries = 0
def result = retries ?: 3
assert result == 3 {code}
In this case, {{0}} is a valid configuration value representing no retries, but
the Elvis operator replaces it with {{{}3{}}}.
Currently, developers must use an explicit null check:
{code:java}
def result = retries != null ? retries : 3 {code}
This introduces unnecessary verbosity for a common use case.
h2. Proposed Syntax
Introduce the {{??}} operator:
{code:java}
def result = value ?? defaultValue {code}
The right-hand expression should be evaluated only when the left-hand
expression evaluates to {{{}null{}}}.
h2. Expected Behavior
{code:java}
assert (null ?? 10) == 10
assert (0 ?? 10) == 0
assert (false ?? true) == false
assert ('' ?? 'default') == ''
assert ([] ?? [1, 2]) == []
assert ([:] ?? [enabled: true]) == [:] {code}
Short-circuit evaluation
The left operand should be evaluated exactly once, and the right operand should
only be evaluated when necessary.
{code:java}
def calls = 0
def fallback = {
calls++
return 10
}
assert (0 ?? fallback()) == 0
assert calls == 0
assert (null ?? fallback()) == 10
assert calls == 1
{code}
h2. Comparison with Existing Elvis Operator
||Value||{{value ?: default}}||{{value ?? default}}||
|{{null}}|default|default|
|{{0}}|default|{{0}}|
|{{false}}|default|{{false}}|
|{{''}}|default|{{''}}|
|{{[]}}|default|{{[]}}|
|Non-empty value|original|original|
h2. Benefits
* Provides explicit null-only fallback semantics.
* Preserves valid falsy values without additional conditional expressions.
* Improves readability and reduces boilerplate.
* Complements the existing Elvis operator rather than replacing it.
* Aligns Groovy with familiar null-coalescing syntax found in languages such
as C#, JavaScript, and PHP.
* Helps avoid subtle configuration bugs involving counters, flags, and empty
values.
h2. Compatibility Considerations
This proposal does not require changing the existing behavior of Groovy Truth,
{{{}?:{}}}, or {{{}?={}}}.
The new operator should work with dynamically typed and statically compiled
Groovy code.
Operator precedence, associativity, interaction with other conditional
operators, and parser compatibility would need to be defined as part of the
implementation.
h2. Optional Future Enhancement
A null-coalescing assignment operator ({{{}??={}}}) could be considered
separately:
{code:java}
def retries = 0
retries ??= 3
assert retries == 0 {code}
The initial proposal focuses only on the {{??}} operator.
h2. Acceptance Criteria
* {{??}} returns the left-hand value whenever it is not {{{}null{}}}.
* {{??}} returns the right-hand value when the left-hand value is {{{}null{}}}.
* The left-hand expression is evaluated exactly once.
* The right-hand expression is evaluated lazily.
* Falsy non-null values are preserved.
* Existing Elvis operator behavior remains unchanged.
* The operator is supported by the Groovy parser and static compilation.
* Tests and language documentation are added.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)