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)

Reply via email to