Bug #12412
closedExtend safe navigation operator
Description
I wonder if we couldn't extend the safe navigation operator to work with any object rather than just nil.
I tend to still use this kind of code in some scenarios, specially when I work with objects with dynamic interfaces or arguments with different possible object types:
class Foo
   def bar(baz)
       if baz.respond_to?(:qux)
          return baz.qux
       end
       'whatever'
   end
end
What if we extend the safe navigation operator to work with any kind of object?
If it doesn't respond to the method it would return nil like this:
class Foo
  def bar(baz)
    baz&.qux || 'whatever'
  end
end
In order to not break backwards compatibility we should keep the current behaviour as well so the rational would be:
nil&.any_method
# => nil
foo&.non_responding_method
# => nil
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      
    
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      - Description updated (diff)
        
           Updated by phluid61 (Matthew Kerwin) over 9 years ago
          Updated by phluid61 (Matthew Kerwin) over 9 years ago
          
          
        
        
      
      Daniel Ferreira wrote:
I wonder if we couldn't extend the safe navigation operator to work with any object rather than just nil.
Making it shorthand for respond_to? isn't an extension, it changes the operator. What would this do under your proposal?
nil&.nil?
Also your "would become" isn't quite the same if bar.qux returns nil or false.
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      Matthew Kerwin wrote:
Daniel Ferreira wrote:
I wonder if we couldn't extend the safe navigation operator to work with any object rather than just nil.
Making it shorthand for
respond_to?isn't an extension, it changes the operator. What would this do under your proposal?nil&.nil?
You're right. That is a challenge. Is it a blocking issue or can we try to overcome that problem?
Also your "would become" isn't quite the same if
bar.quxreturnsnilorfalse.
It is not quite the same but what is the issue?
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      In my proposal
nil&.nil? 
# => true
Which is different from the current behaviour. You're right.
Maybe we can add that constraint.
If nil return nil making it:
nil&.nil?
# => nil
nil&.class
# => nil
nil&.foo
# => nil
It would be a compromise.
That way there would be backwards compatibility.
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      - Description updated (diff)
        
           Updated by nobu (Nobuyoshi Nakada) over 9 years ago
          Updated by nobu (Nobuyoshi Nakada) over 9 years ago
          
          
        
        
      
      It's a rejected behavior in the former discussions.
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      I was reading the safe navigation issue full discussion and didn't find a clear reason why we should not extend the behaviour.
Is it the fact that for the other languages it only applies to nil?
        
           Updated by dsferreira (Daniel Ferreira) over 9 years ago
          Updated by dsferreira (Daniel Ferreira) over 9 years ago
          
          
        
        
      
      Another improvement to make it even more powerful.
Introduction of a new global variable that would return the object which didn't respond to the method breaking the safe navigation chain.
Although we need to think about concurrency as well and performance.
Just an idea:
foo = ''
def foo.bar
   1
end
foo&.bar&.baz
# => nil
$NEW_GLOBAL_VARIABLE_OR_SOMETHING_ELSE
# => 1
        
           Updated by matz (Yukihiro Matsumoto) over 9 years ago
          Updated by matz (Yukihiro Matsumoto) over 9 years ago
          
          
        
        
      
      - Status changed from Open to Rejected
Although I don't say your idea is totally bad, it's not good to add it to Ruby for several reasons:
- it's not compatible
- introducing a new global variable is a terrible idea for any reason
- it would be slower by using respond_to? in run time
- it will hinder future type inference which is supposed to be introduced in Ruby3
Matz.