Does Ruby Have Syntax for Safe Navigation Operator of Nil Values, Like in Groovy

Does Ruby have syntax for safe navigation operator of nil values, like in Groovy?

In a rails app there is Object#try

So you can do

obj1.try(:obj2).try(:value)

or with a block (as said on comments bellow)

obj.try {|obj| obj.value}

UPDATE

In ruby 2.3 there is operator for this:

obj&.value&.foo

Which is the same as obj && obj.value && obj.value.foo

Why does Ruby use its own syntax for safe navigation operator?

This answer is based on the discussion of the feature request in Ruby's issue tracking. According to Ruby's author Yukihiro Matsumoto it wouldn't be possible to introduce operator ?. in Ruby because foo? is valid method name and thus it couldn't be parsed. The first candidate for operator was reversed sequence .?. That syntax was already implemented (by Nobuyoshi Nakada) but was later discarded as it was thought to be too close to original syntax introduced by the other languages (that was not feasible as mentioned earlier). The final syntax &. was accepted as suggested by Matsumoto.

Here's the justification for this syntax given by Matsumoto

I think about this for a while, and thinking of introducing &. instead of .?, because:

  • .? is similar to ?. in Swift and other languages, but is different anyway.
  • Since ? is a valid suffix of method names in Ruby, we already see a lot of question marks in our programs.
  • u&.profile reminds us as short form of u && u.profile.

But behavior of &. should be kept, i.e. it should skip nil but recognize false.

This syntax was then released as part of Ruby 2.3.0-preview1.

Is there an equivalent null prevention on chained attributes of groovy in ruby?

This works in Rails:

my_object.try(:name).try(:capitalize)

If you want it to work in Ruby you have to extend Object like this:

class Object
##
# @person ? @person.name : nil
# vs
# @person.try(:name)
def try(method)
send method if respond_to? method
end
end

Source

In Rails it's implemented like this:

class Object
def try(*a, &b)
if a.empty? && block_given?
yield self
else
__send__(*a, &b)
end
end
end

class NilClass
def try(*args)
nil
end
end

How to Groovy-ify a null check?

Typically for null-checking I reach for ?: (elvis operator, returns a default value if the left-hand side is null or resolves to false) or ?. (safe navigation, evaluates to null if left-hand side is null). If you want to set a default value to use when a property is not present you can do this:

def myVar = System.properties['props'] ?: 'mydefaultvalue'

which sets myVar to 'mydefaultvalue' if there is nothing found in System.properties for the key 'props' (or if the value returned resolves to false).

But since the default value in your case is null then

def myVar = System.properties['props']

would do the job as well, because when nothing is found for the given key then null is returned.

The Groovy-ifications here are:

  • prefer single-quoted strings to double-quoted ones if you don't need GroovyString interpolation

  • use indexing-with-brackets syntax for maps and lists (instead of 'get' or 'put')

  • use the shortened property form (without the get prefix) if the getter has no arguments (unlike Java, Groovy implements the universal access principle); System.getProperty(String) is a convenience for Java programmers but it's unneeded in Groovy

  • shorten default-if-null cases with ?:

This idiom found in JavaScript using || :

def myVar = System.properties['props'] || 'mydefaultvalue'

doesn't work in Groovy. The result of a boolean test is a boolean, so myVar gets set to true.

Best Scala imitation of Groovy's safe-dereference operator (?.)?

How about this?

def ?[A](block: => A) =
try { block } catch {
case e: NullPointerException if e.getStackTrace()(2).getMethodName == "$qmark" => null
case e => throw e
}

Using this little snippet, you can dereference safely and the code itself is quite succinct:

val a = ?(b.c.d.e)

a == null if b or b.c or b.c.d or b.c.d.e is null, otherwise, a == b.c.d.e

I think the value of a safe-dereference operator is diminished when you are using a language like Scala which has facilities like call-by-name and implicits.

ps: I modify the code above a bit in light of one of the comments below to handle the case when NullPointerException is
actually thrown inside the called function.

BTW, I think using the function below is a more idiomatic way of writing Scala:

def ??[A](block: => A): Option[A] = ?(block) match {
case a: A => Some(a)
case _ => None
}

like so:

??(a.b.c.d) match {
case Some(result) => // do more things with result
case None => // handle "null" case
}

How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?

Ruby 2.3.0 introduced a new method called dig on both Hash and Array that solves this problem entirely.

name = params.dig(:company, :owner, :name)

It returns nil if the key is missing at any level.

If you are using a version of Ruby older than 2.3, you can use the ruby_dig gem or implement it yourself:

module RubyDig
def dig(key, *rest)
if value = (self[key] rescue nil)
if rest.empty?
value
elsif value.respond_to?(:dig)
value.dig(*rest)
end
end
end
end

if RUBY_VERSION < '2.3'
Array.send(:include, RubyDig)
Hash.send(:include, RubyDig)
end


Related Topics



Leave a reply



Submit