I like judicious use of the ternary, conditional operator. To my mind it's quite succinct.
However, in ruby, I find I'm often testing predicate methods, which already have their own question marks:
some_method( x.predicate? ? foo : bar )
I'm jarred by those two question marks so close to each other. Is there an equivalently compact and readable alternative?
The reason why the conditional operator is needed in C, is because the conditional statement is, well, a statement, i.e. it doesn't (and cannot) return a value. So, if you want to return a value from conditional code, you're out of luck. That's why the conditional operator had to be added: it is an expression, i.e. it returns a value.
In Ruby, however, the conditional operator is completely superfluous because there are no statements is Ruby anyway. Everything is an expression. Specifically, there is no if statement in Ruby, there is only an if expression.
And since if is an expression anyway, you can just use it instead of the cryptic conditional operator:
some_method( if x.predicate? then foo else bar end )
The only thing you have to remember is that the predicate needs to be terminated by either a newline, a semicolon or a then. So, the first three times you do this, you will turn
if cond
foo
else
bar
end
into
if cond foo else bar end
and wonder why it doesn't work. But after that, the then (or semicolon) will come naturally:
if cond; foo else bar end
if cond then foo else bar end
The closest succinct expression you can get is
x.predicate? && foo || bar
which acts sort of a ternary operator, but more cryptic and ugly.
It's just a case of syntactic diabetes caused by the sugar on the query? methods. I guess we'll just have to learn to live with it.
Just remove the space.
some_method( x.predicate?? foo : bar )
Is equally valid in Ruby. Try it!
Can't say it's easier, but in many cases it might be useful just to wrap predicate itself into brackets, like this:
some_method((x.predicate?) ? foo : bar )
It helps, especially when your predicate is not as simple as method?
Related
I've got constructs like the following that Rubocop complains about that I'm trying to appease
unless foo && !foo.empty? ...
(item exists and is not empty)
and
bar && !bar.positive?
(item exists and is not positive)
In both cases, Rubocop throws a Style/SafeNavigation and says I should use &. instead. However, there does not seem to be a way to write it.
In both cases part of the problem is that there is no concise way of expressing the opposite function - there is no "not empty" for strings, and no "negative or zero" for numbers. Writing bar&.negative? returns the opposite result for zero, for example.
In the other case, writing unless !foo&.empty? has Rubocop complain about unless+a negation, but again there's no way to rewrite it as an if foo&. with any operation that exists.
(btw this is straight Ruby, no Rails, so I don't have blank? and present?)
This:
next unless foo && !foo.empty?
Could be replaced with:
next if foo.nil? || foo.empty?
And similarly, this:
next unless bar && !bar.positive?
Could be replaced with:
next if bar.nil? || bar.positive?
The real code smell here, in my opinion, was the next unless ... not. The double negative indicates that there's probably a cleaner way to write it.
This is an oversight in RuboCop, because the suggested correction can not be guaranteed to have the same semantics. It will be fixed in the upcoming 0.50.1 patch, and the cop will ignore cases with negation.
In the meanwhile, if you're dealing with conditions where your variable could be nil, one option is to use the fact that nil responds to the coercion methods. Example, assuming foo is either an array or nil:
# With nil check
unless foo && !foo.empty?
# With array coercion
if foo.to_a.empty?
For example, I want to make Object#rescue another name so I can use in my code like:
def dangerous
something_dangerous!
dont_worry # instead of rescue here
false
end
I tried
class ::Object
alias :dont_worry :rescue
end
But cannot find the rescue method on Object:
`<class:Object>': undefined method `rescue' for class `Object' (NameError)
Another example is I would like to have when in the language to replace:
if cond
# eval when cond is truthy
end
to
when cond
# eval when cond is truthy
end
Is it possible to give a Ruby keyword alias done in Ruby?
Or I need to hack on Ruby C source code?
Thanks!
This is not possible without some deep changes to the Ruby language itself. The things you describe are not methods but keywords of the language, i.e. the actual core of what is Ruby. As such, these things are not user-changeable at all.
If you still want to change the names of the keywords, you would at least have to adapt the language parser. If you don't change semantics at all, this might do it as is. But if you want to change what these keywords represent, things get messy really quick.
Also note that Ruby in itself is sometimes quite ambiguous (e.g. with regards to parenthesis, dots, spacing) and goes to great length to resolve this in a mostly consistent way. If you change keywords, you would have to ensure that things won't get any more ambiguous. This could e.g. happen with your change of if to when. when is used as a keywords is case statements already and would thus could be a source of ambiguity when used as an if.
If I define an operator,
class Object
def ~#
self || ErrorlessNil.new
end
end
how can I make it so that the ~ is evaluated first, instead of last? Right now, something like
~[1][100].undefined_method
will throw an error, while
(~[0][22]).randomsadfldsf
works fine. The goal is to define a tool that works like coffeescript's question mark. I could make a ?? method but I can't start with a ?, and _? works ok, but that is not fun.
Jörg W Mittag's answer provides one reason it will not work like you want, but there is another reason.
If ~ had stronger precedence than other method application, then
~[0][22].randomsadfldsf
would not be interpreted as
(~[0][22]).randomsadfldsf
but as
(~[0])[22].randomsadfldsf
You can't.
In Ruby, you can only override the behavior of existing operators. You cannot define new operators nor can you change their precedence, arity, associativity or fixity.
This is unlike, for example, Haskell or Fortress, which allow you to define your own operators with your own fixity (prefix, postfix, infix), associativity (left, right, none) and precedence. Ruby is like Python, C# and C++ in this regard.
Obvious and easy answer is that, aside (), you cannot change operator precedence.
However you REALLY want, you can use eval* or/and you can build your Abstract syntax tree (AST)/SEXP.
str = "~[1,2,3].first"
def foo str
if str[0] == '~'
obj, meth = str[1..-1].split '.'
eval "(~#{obj}).#{meth}"
end
end
foo str
foo require that str is in this format:
"<~><object/literal without dot><method without dot>"
With more complex code, foo becomes more complex. You will end with some Regexp or with AST/SEXP.
Build-in Ruby Ripper can change source into sexp. There is gem named Sorcerer. It can change Sexp from the Ripper back to source. However it was tested with 1.9.3 and 2.0 only.
*eval may be insecure because it may run code that you don't want to. Check you string before evaling it!
In ruby you can do:
do_stuff if foo == bar
Technically speaking, is there a &block to capture the block of code before if? how does this stuff work?
They aren't methods, they are keywords. Their behavior is defined by the language specification and not restricted by the rules of the language. They can do anything they want.
In order to write more concisely, rather than do this:
test_value = method_call_that_might_return_nil()
if test_value
do_something_with test_value
end
I've been assigning in the conditional:
if test_value = method_call_that_might_return_nil()
do_something_with test_value
end
Is this bad style? The still-more-concise syntax:
do_something_with test_value if test_value = method_call_that_might_return_nil()
is not allowed, as discussed in another SO question, and will remain that way in 1.9, according to Matz (http://redmine.ruby-lang.org/issues/show/1141).
Given the possible confusion of assignment and comparison, does this make it too hard to read the code?
It is GOOD style to use assignments in conditionals. If you do so, wrap the condition in parentheses.
# bad (+ a warning)
if v = array.grep(/foo/)
do_something(v)
# some code
end
# good (MRI would still complain, but RuboCop won't)
if (v = array.grep(/foo/))
do_something(v)
# some code
end
# good
v = array.grep(/foo/)
if v
do_something(v)
# some code
end
See the community style guide for more information
One somewhat widespread idiom is to use and, which would look something like this:
tmp = method_call_that_might_return_nil and do_something_with tmp
Another possibility would be to call #nil? explicitly, that way the intent becomes a little bit clearer; in particular it is really obvious that you actually meant to assign instead of compare:
unless (tmp = method_call_that_might_return_nil).nil?
do_something_with tmp
end
Concise code is not necessarily better code. Concision is useful when it improves the communication of intended code behavior from author to future maintainers. I think enough of us come from backgrounds in which we've had accidental assignments in if blocks (when we meant to have an equality comparison) that we prefer styles in which it's absolutely clear that assignment is meant, rather than comparison. The .nil? idiom already mentioned has that property, and I'd consider it cleaner than having the bare assignment inside the if condition. Really, though, I don't see the harm in having the extra line of code for the assignment.
The functional-programming way to do this is to use andand. It's a readable way of chaining method calls so that a nil in the middle stops the chain. So your example would be something like:
method_call_that_might_return_nil.andand.tap {|obj| do_something_with obj}
## or, in the common case: ##
method_call_that_might_return_nil.andand.do_something
Yeah, I would say it's bad style due to the possible confusion between assignment and comparison. It's only one more line to assign and then test, and it avoids having someone in the future think that was a bug and patch it to use == instead.
C programmers do this a lot. I don't see a problem with it in Ruby either so long as it's clear what's happening.
I think it's fine. Aversion to assignment in a condition comes from knowing that a missed key stroke when typing == turns a comparison into an unintended assignment. A stylistic prohibition on using assignment in a condition makes such accidents stand out like to the eye (and sometimes to the language, as in C, where many compilers can be made to emit a warning if they encounter an assignment in a condition). On the other hand, tests also make such accidents stand out. If your code is well covered by tests, you can consider discarding such prohibitions.
Due to the warning, performing the assignment in the if clause has a quite pungent smell. If you do have an else case to handle then the case ... in ... pattern matching can offer something:
case method_call_that_might_return_nil
in nil
# handle nil case
in test_value # pattern match to a new variable
# handle non-nil case with return value of method assigned to test_value
end
Or...
case method_call_that_might_return_nil
in test_value if test_value # pattern match to a new variable if not nil
# handle non-nil case with return value of method assigned to test_value
else
# handle nil case
end