3

I am trying to compare a number literal with the return value of a function that could return nil or a numeric. Consider this:

def unreliable
  [nil, 42].sample  
end

unreliable > 10

This will blow up 50% of the time with NoMethodError: undefined method '>' for nil:NilClass. So I tried this:

unreliable&.(:>, 10)

That approach does the nil guarding I expect, but I get this when unreliable returns 42:

NoMethodError: undefined method `call' for 42:Fixnum

I suspect this has to do with the quirks of allowing only one instance to exist for each Numeric, see here. And I know I can do this:

foo = unreliable
foo && foo > 10

But is there a way to use the safe navigation operator with a numeric and :>, :<, :==, :+, :-, :/, :*, etc?


Edit: The focus on Numeric in my question is a red herring. See @Jörg's answer. I was confusing Rails's try syntax with the safe-navigation operator's syntax.

Ethan Kent
  • 381
  • 1
  • 4
  • 20
  • Aha, okay—sorry about that. I was trying to express gratitude and let folks know that the answer solved my problem, but I see what you're saying and will do that going forward. – Ethan Kent Apr 20 '17 at 17:06
  • @CarySwoveland The answer here is perfectly fine. Stop trying to police things that are best left alone. – Darth Egregious Apr 20 '17 at 17:22
  • @Fuser97381, did I say the answer was wanting? It's an excellent answer. I would have offered the same advice had it been my answer. – Cary Swoveland Apr 20 '17 at 17:30

2 Answers2

5

This works fine in Ruby 2.3+ :

unreliable&.> 10

For example :

[-5, 0, nil, 5].each do |unreliable|
  p unreliable&.> 0
end
# false
# false
# nil
# true

The way you tried it, Ruby expects unreliable to be a callable object such as a Proc :

unreliable = Proc.new{ |*params| puts "unreliable has been called with #{params}" }
unreliable&.(:>, 10)
# unreliable has been called with [:>, 10]
unreliable.call(:>, 10)
# unreliable has been called with [:>, 10]
unreliable&.call(:>, 10)
# unreliable has been called with [:>, 10]
unreliable[:>, 10]
# unreliable has been called with [:>, 10]

With the safe-navigation operator, there's no need to put parens and the method should be a method name, not a symbol (Rails' try expects a symbol).

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • For the record, `p unreliable&.send( :>, 0)` also works. Very minor, but I would be inclined to write your operative line `p unreliable&.> 0` because the presence of the period suggests you are not using syntactic sugar. – Cary Swoveland Apr 20 '17 at 17:15
  • @CarySwoveland So without a space between `&.` and `>`? I'm open to suggestions since I couldn't find any pleasant way to format the line. – Eric Duminil Apr 20 '17 at 17:17
  • That's my tilt. You could of course write `p unreliable&.>(0)` but I'd only consider that if you had something else following on the line (`p unreliable&.>(0) && ...`. – Cary Swoveland Apr 20 '17 at 17:18
  • @CarySwoveland: Thanks. I generally find the safe-nav operator unpleasant to write and read. :-/ – Eric Duminil Apr 20 '17 at 17:19
  • Me too, but maybe we'll feel differently when we've used it more. – Cary Swoveland Apr 20 '17 at 17:21
  • Readers: Eric originally had `unreliable&. > 10`, which was fine. I mention this only to point out that the space after the period is optional. – Cary Swoveland Apr 20 '17 at 17:24
  • I realize where my miscue came from: I was going by analogy to the Rails method `try`, which takes the kinds of parameters I was trying to use. I assume the clunkier Rails syntax reflects that implementing something like the safe-navigation operator requires access to the guts of the language itself. – Ethan Kent Apr 20 '17 at 17:29
1

I suspect this has to do with the quirks of allowing only one instance to exist for each Numeric

No, this has nothing to do with that at all.

foo.(bar)

is syntactic sugar for

foo.call(bar)

Ergo,

foo&.(bar)

is syntactic sugar for

foo&.call(bar)

So, your code:

unreliable&.(:>, 10)

is syntactic sugar for

unreliable&.call(:>, 10)

I'm not sure who told you that the safe-navigation operator takes the message as a symbol argument. The whole point of the safe-navigation operator is that you only have trivial syntactic overhead by adding a single character, the & in front of the ., and the expression is otherwise unchanged.

So,

unreliable > 10

which is the same as

unreliable.>(10)

simply becomes

unreliable&.>(10)
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653