I have the following logic:
if something
some_variable = true if another_variable
your_variable = true if that_other_variable
elsif thatthing
my_variable = true if another_variable
her_variable = true if that_other variable
else
puts "nothing found"
end
This is extremely simplified logic. Please forgive in advance for the names of the vars.
Coming from another language, I have conditioned my mind to think that nested inline if conditions are bad for readability reasons. I also have biases towards truthiness type tests. I tried to avoid negation if possible, but some times its unavoidable.
That being said, I have a couple of common possibilities of what I could use to replace the inline ifs, and one that is more uncommon that I put together to avoid negation. I am looking for feedback, and some things that you are doing to address this. Here it goes:
Possibility 1:
ternary:
if something
some_variable = defined?(another_variable) ? true : false
Possibility 2:
negation:
if something
some_variable = !defined?(another_variable)
# There are some variations to this, but this helps to address the negation I am trying to avoid.
Possibility 3 (I am favoring this one, but it seems a little uncommon):
if something
some_variable = (defined?(another_variable) && another_variable.length > 0) # Two truth based tests (does the var exist, and is it greater than 0 in length) This returns true/false.
Please let me know if anyone has any other solutions, and also which they feel is the most idiomatic.
Thank you!
Okay, instead of trying to stuff my answers in comments, I'll just give an answer, even though it's not quite an "answer".
All of your alternatives are not equivalent, they all do different things. What are you actually trying to do?
some_variable = true if another_variable
Will set some_variable = true only if another_variable has a truthy value. In ruby, any value but nil or false is evaluated as true in a boolean test. another_variable has to already exist, otherwise you'll get an exception raised.
So if another_variable exists, and is anything but nil or false, you'll set some_variable to true.
If another_variable exists and is nil or false, you won't change the value of some_variable at all, it will remain unchanged. It will not be set to false. (if some_variable didn't already exist, it will be brought into existence with a nil value. Otherwise it's previous value will be unchanged).
If another_variable didn't exist at all, the statement will raise an exception.
Is that what you intend? (And incidentally, using these meaningless variable names makes it a lot more confusing to talk about your example).
If that's really what you intend (and I'm doubting that it is), then there's probably no shorter or clearer way to do it than the way you've done: some_variable = true if another_variable.
There's nothing un-idiomatic with a one-line trailing if in ruby, it's perfectly idiomatic, people do it all the time, when it makes the code more clear. In your example -- and again it's hard to tell from your fake example -- I don't think it's the one-line if that makes it hard to tell what's going on, it is the weird structure of the top-level conditions, the overall structure of the code itself, it's very difficult to tell what the heck it's supposed to be doing. But you say you don't want to refactor that, so okay. And maybe that's just an artifact of you trying to produce a simple hypothetical and coming up with an awfully confusing one, maybe the original code isn't as confusing.
Your other alternative examples do something else, they are not equivalent. For instance:
some_variable = defined?(another_variable) ? true : false
This has different semantics than your first example.
if another_variable exists at all, then some_variable will be set to true. Even if another_variable exists with the value nil or false. So long as another_variable exists, some_variable will be set to true.
If another_variable does not exist at all some_variable will be set to false.
There is no case where some_variable will be untouched.
If that's really what you intend to do, then there certainly is a more concise alternative. defined? returns nil if the variable is not defined, otherwise a string explaining the nature of the defined variable. If you want to turn that into strictly boolean true or false, you can use a double boolean negation: some_variable = !! defined?(another_variable). Recall that in ruby anything but false or nil is evaluated as truthy in a boolean context, so !! foo will turn into false if foo was nil or false, and true if foo was anything else.
People also just commonly do some_variable = defined?(another_variable) and figure that's good enough to capture "did another_variable exist", since some_variable will still be evaluated the same in a boolean test. Although really, in idiomatic ruby you only occasionally need defined? at all, mostly you are working with variables you already know exist, and you don't need to test to see if they do, and even more seldom do you need to store the results of that test in a variable.
Your additional examples all have yet different semantics again, none of them are actually equivalent. Hopefully I've given you the tools here to see what they all actually do. You can also try them all out in a little test script, or in the irb REPL.
I suspect neither of these are what you actually want to do. It depends on the actual semantics you want, which you haven't told us. If your first version you start out with actually does represent the semantics you want, then I think there's no shorter way to do it.
You say you are refactoring existing code -- Based on this example you give us, I think the existing code may likely be a mess, with lots of bugs in it, written strangely, and with lack of certainty about what it's even supposed to do, since you aren't the original developer. I'm guessing it doesn't have any tests either. In this situation, I'd start by writing some tests establishing what the code is supposed to do, and then you can try refactoring and make sure it still performs as expected. Without that, and with such messy code, and especially with being newish to ruby, your refactoring is highly likely to change the semantics of the program when you weren't intending to, likely introducing new and even stranger bugs.
You don't need that true if <var> part and can use !!instead:
if something
some_variable = !!another_variable
your_variable = !!that_other_variable
elsif thatthing
my_variable = !!another_variable
her_variable = !!that_other variable
else
puts "nothing found"
end
Related
h = {a: "foo"}
h.fetch(:a, h.fetch(:b))
yields key not found: :b
It seems strange that Ruby evaluates the default value even when the key is found? Is there a way around this?
Edit: Apparently this behavior falls under the paradigm of lazy vs eager evaluation. Nearly all imperative languages use eager evaluation, while many functional languages use lazy evaluation. However some languages, such as Python (which was before last week the only language I knew), have lazy evaluation capabilities for some operations.
It seems strange that Ruby evaluates the default value even when the key is found? Is there a way around this?
The overwhelming majority of mainstream programming languages is strict, i.e. arguments are fully evaluated before being passed. The only exception is Haskell, and calling it mainstream is a bit of a stretch.
So, no, it is not really "strange", it is how (almost) every language works, and it is also how every single other method in Ruby works. Every method in Ruby always fully evaluates all its arguments. That is, why, for example defined? and alias cannot be methods but must be builtin language constructs.
However, there is a way to delay evaluation, so to speak, using blocks: the content of a block is only evaluated each time it is called. Thankfully, Hash#fetch does take a block, so you can just use
h.fetch(:a) { h.fetch(:b) }
If you want the computation to only run if the key is not found, you can use the alternate form of fetch which accepts a block:
h.fetch(a) { h.fetch b }
I didn't actually know this was the case but I had a hunch and tried it and it worked. The reason I thought to try this was that something similar can be done in other methods such as gsub, i.e.
"123".gsub(/[0-9]/) { |i| i.to_i + 1 } == "234"
You could use the || operator instead, since it's lazy evaluated by design.
For example the following code where Nope is not defined does not throw an error.
{ a: "foo" }[:a] || Nope
However the fetch version will throw an error.
{ a: "foo" }.fetch(:a, Nope)
Personally I prefer how fetch is designed because it wont hide a bug in the default value.
However, in cases where I would rather not evaluate a default statement, then I would definitely reach for the || operator before doing something in a block or a lambda/proc.
This is quite a quick question. I currently use*
do_this if (testvar ||= false)
Which works just fine.
But this approach unnerves me because while fast, it does not short-circuit like defined?(testvar) && testvar does, and it instantiates and assigns a value to a local variable that is subsequently never used, which seems inefficient.
I enjoy the very reasonable fact that instance variables are nil before assignment, but I'd like for the situation to be just as easy for local variables.
EDIT: * – that's a huge lie. I don't know why I said that. I don't use testvar ||= false. That would be silly. What I have used, however, is testvar rescue false, which doesn't have nearly as many ugly side effects (does it?).
I don't know about a "fancy" way but there's an explicit way, which is sometimes better than fancy, IMHO.
do_this if defined? testvar and testvar
How about:
do_this unless testvar.nil?
Is it normal for methods with a question mark to return something that's truthy (for example, a number) to indicate that something is true, or should true itself be returned?
Are there any examples of truthiness being used in the Ruby standard library or by Rails, for example?
Background: Someone wrote a String#int? method in an answer to a separate question, which returned an integer to represent true, and nil to represent false. Another user was surprised at not returning a boolean.
Adding a ? to a method name in Ruby is idiomatic that the method will return true or false. Object#nil? is a good example. In fact, Object has a lot of good examples of truthiness checks.
It is usual for methods ending with ? to return either true or false but it is not systematic and no core method will assume it.
An example in the core classes is Numeric#nonzero? which never returns true or false.
42.nonzero? # => 42
The library Set has add? and delete? too. I wish Enumerable#one? returned nil or false to distinguish cases where the count is zero from when it is greater than one.
A similar example are the comparison operators (<, >, ...), which usually return only true or false. Here again exceptions exist for Module's operators that will return nil instead when the two modules are not related:
Array > Enumerable # => false
Array > Fixnum # => nil
There are two answers to your question, and both are valid:
Technically, anything returning a false or nil value acts as a false boolean when doing a conditional test, just as a non-nil or true value acts as a true. So, the method in question will work correctly for most times you'd want to know if something is an integer.
But stylistically a method that ends with '?' should return either a Boolean true or false and only those.
The method in question doesn't really play nicely with our expectations and fails the POLS ("principle of least surprise") because you can reasonably expect a Boolean value being returned, and get an integer or a nil. THAT could lead to unpleasant surprises in code when it fails with an unexpected nil or a Fixnum value.
So, while it's a valid method, it's not a good method, and I would have brought it up in a code review. And that leads to a separate discussion of how subtle things like that can enhance or hurt code maintenance, but that's an entirely different discussion.
I renamed the method in question to remove the ?, which was really not important to the question being answered. Scanning through the core functions that end in ?s, the only ones I spotted that returned data or nil were the add? and delete? methods on Set.
Is the keyword unless the same as if?
When do you use ??
I've seen:
if someobject?
I know it checks against nil correct?
Is the keyword 'unless' the same as 'if' ?
No, it's the opposite.
unless foo is the same as if !foo
if someobject?
I know it checks against nil correct?
No it calls a method named someobject?. I.e. the ? is just part of the method name.
? can be used in methodnames, but only as the last character. Conventionally it is used to name methods which return a boolean value (i.e. either true or false).
? can also be used as part of the conditional operator condition ? then_part : else_part, but that's not how it is used in your example.
unless is actually the opposite of if. unless condition is equivalent to if !condition.
Which one you use depends on what feels more natural to the intention you're expressing in code.
e.g.
unless file_exists?
# create file
end
vs.
if !file_exists?
# create file
end
Regarding ?, there is a convention for boolean methods in Ruby to end with a ?.
This statement:
unless conditional expression
Is the equivalent to:
if not (conditional expression)
In Ruby you can end your method names with a question mark which is normally used to show that it is a boolean method.
With Rails a check against nil would look like this:
someobject.nil?
This calls the nil?() method of the object, which returns true for NilObject and false for anything else.
I think the convention for ?-suffix is to use it when naming a method that returns a boolean value. It is not a special character, but is used to make the name of the method easier to understand, or at least I think that's what the intention was. It's to make it clear that the method is like asking a question: it shouldn't change anything, only return some kind of status...
There's also !-suffix that I think by convention means that the method may have side-effects or may modify the object it is called on (rather than return a modified copy). Either way, the ! is to make you think carefully about calling such a method and to make sure you understand what that method does.
I don't think anything enforces these conventions (I've never tried to break them) so of course you could abuse them horribly, but your fellow developers would not be happy working with your code.
for unless see here: http://railstips.org/blog/archives/2008/12/01/unless-the-abused-ruby-conditional/
if someobject?
The appending of a '?' here only means that it returns a boolean.
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