Read this post ‘unless’ you’re not a Ruby developer(jesseduffield.com)
jesseduffield.com
Read this post ‘unless’ you’re not a Ruby developer
https://jesseduffield.com/Unless/
311 comments
I really don’t understand the author’s point about adding extra conditions. They admit that many people will find a single condition with `unless` more readable. They then complain that it becomes unreadable when adding another condition. OK, so swap it out for an `if` at that point. No one is forcing you to keep using `unless` if the requirements change. “You should use a suboptimal solution to cater for unknown future requirements” is a terrible argument.
As the article starts to explore, it's more useful in the context of a single line conditional with a single boolean to evaluate, or what I sometimes call a "dangling conditional". This is valid ruby:
The whole point of Ruby - the only reason it really exists - is that it should be fun and easy and not require you to spend too much time stressing over rules. If you read a piece of Ruby and don't like it, change it. It's not Python - there is more than one way. Use the way that makes sense in the context you're using it.
If you can't trust yourself or your team to do that wisely in a project, consider changing languages, because guess what? The language isn't going to change because you don't like that thing.
do_something if boolean_expression
All unless does is allow you to negate it using an expression that's more natural to most people do_something unless boolean_expression
To me the negation of an if with ! is less clear in this context. For example: puts "That's a prime" if is_prime?(variable)
makes sense. So does: puts "That's not a prime!" unless is_prime?(variable)
Far more so than: puts "That's not a prime!" if !is_prime?(variable)
The author decides however to add more conditionals. Fine, don't do that. Or that it's unreadable in some contexts. Fine, don't do that either.The whole point of Ruby - the only reason it really exists - is that it should be fun and easy and not require you to spend too much time stressing over rules. If you read a piece of Ruby and don't like it, change it. It's not Python - there is more than one way. Use the way that makes sense in the context you're using it.
If you can't trust yourself or your team to do that wisely in a project, consider changing languages, because guess what? The language isn't going to change because you don't like that thing.
I've been writing Ruby for 20+ years and "unless" (the whole language, actually) immediately clicked for me when I first learned it.
That said, I only use it in 2 cases:
* as a trailing condition (do_something unless this), mainly for early returns * as the only arm of a multi-line block (unless this ... end, no "else" blocks - Rubocop would yell at me anyway)
And then only if the condition is either a simple value, or a combination of simple values (this && that, this || that).
That's it. Never had a problem with double negatives or accidentally inverting the logic.
That said, I only use it in 2 cases:
* as a trailing condition (do_something unless this), mainly for early returns * as the only arm of a multi-line block (unless this ... end, no "else" blocks - Rubocop would yell at me anyway)
And then only if the condition is either a simple value, or a combination of simple values (this && that, this || that).
That's it. Never had a problem with double negatives or accidentally inverting the logic.
I don't have a problem with double negation but I hate ruby for this kind of design - pointless aliases for everything.
It's the exact opposite of pythons "There should be one– and preferably only one –obvious way to do it" - they intentionally create solutions that have zero practical benefit - it's just fuels arguments based on preferences and introduces mental overhead due to inconsistency.
It's the exact opposite of pythons "There should be one– and preferably only one –obvious way to do it" - they intentionally create solutions that have zero practical benefit - it's just fuels arguments based on preferences and introduces mental overhead due to inconsistency.
I had to read the title five times to decide if I was supposed to read it. I'm sold.
The worst thing about `unless` and double negatives, is having been raised in a culture with different double negative rules than English. In Italian a double negative is still negative.
I know boolean logic pretty well, but `unless !something` still trips me up to this day.
I know boolean logic pretty well, but `unless !something` still trips me up to this day.
When I write ruby, and I love ruby, I don't do too much condition branching of logic.
- `if` and `unless` are for guards. The meat of every method, (the happy path) is at the bottom of that method.
- If there are legitimately two options that branch on a conditional, I try to make sure that both branches return the same type. (feature flags, a/b feature testing, etc.)
- If I can refactor code to a case statement, I will 95% of the time
This article screams, "I don't use a linter" or "I work at a company that doesn't have a style guide."The real problem is that all negatives make it harder to understand things. They open the door for double (and triple) negatives to find their way into the code, and then bang: The only person who can read it is the person who wrote it.
Since unless has a not built into it, it has a lot of potential to confuse people. In my experience, guard clauses are the only place where they make sense.
Since unless has a not built into it, it has a lot of potential to confuse people. In my experience, guard clauses are the only place where they make sense.
def mute_mic
return unless mic_active?
..
end
In this sense, you know that the entirety of the function is an expected (positive) case.IMHO, 'unless' in Ruby works best for single checks. It's just a convenient form of 'if not'. That's about it.
I always insist on not using 'unless' if it's not clearly readable as an english phrase or if there's an 'else' as well.
A similar pattern I follow with 'if !'. If there's an 'else' condition always put the positive check first and the negative one in the else, rather than the other way around.
Footguns are equally possible using complex 'if !' statements.
I always insist on not using 'unless' if it's not clearly readable as an english phrase or if there's an 'else' as well.
A similar pattern I follow with 'if !'. If there's an 'else' condition always put the positive check first and the negative one in the else, rather than the other way around.
Footguns are equally possible using complex 'if !' statements.
We use it exclusively for guard statements at the beginning of a function, and we keep it to a single statement per line
return unless token.present? return unless user.valid? return unless foo = get_foo(user)
Its extremely readable and easy to grok imo.
return unless token.present? return unless user.valid? return unless foo = get_foo(user)
Its extremely readable and easy to grok imo.
There are two main reasons unless is great and I miss it now that I primarily program in python.
1. It is a negative, and that is a great thing when there are boolean conditions. Yes, you can wrap the whole thing in brackets, but when doing a visual scan quickly over code, they're easier to miss than the giant `unless` token which guarantees that you won't have an early closing bracket. The more the number of terms go up, the more I have to be fastidious with watching where brackets begin and end.
2. It hints to the expected flow of things. Isn't the first more readable to you?
Basically my argument boils down to this:
Bears. Beats. Battlestar Galactica.
1. It is a negative, and that is a great thing when there are boolean conditions. Yes, you can wrap the whole thing in brackets, but when doing a visual scan quickly over code, they're easier to miss than the giant `unless` token which guarantees that you won't have an early closing bracket. The more the number of terms go up, the more I have to be fastidious with watching where brackets begin and end.
2. It hints to the expected flow of things. Isn't the first more readable to you?
return :allow_air_travel unless self.nuclear_war_ongoing?
return :allow_air_travel if ! self.nuclear_war_ongoing?
Or I'll put it another way. If you do a search of your own hacker news comments on BigQuery, will you never find the english word "unless" outside of a Ruby discussion there? You could say "if not" why bother using an extra word? Or what about "not true" do you ever say that when you could just say "false" like Dwight from The Office?Basically my argument boils down to this:
Bears. Beats. Battlestar Galactica.
"Unless" should only be used when the imperative condition is simple.
You don't understand Ruby if you think there is something wrong with trying to have more than one option to do things, imperfect shortcuts and more human-readable code.
Unless works really well when there is one named boolean variable after it and it's named correctly or if the condition is simple to understand.
It also works well when used inline and without an else statement. For other cases "if" is better-suited. But then again "unless" is another option in your tool chain that makes for a more elegant programming language if you're not a stickler and not constantly finding yourself pining for a platonic language.
It also makes code read better in English.
You don't understand Ruby if you think there is something wrong with trying to have more than one option to do things, imperfect shortcuts and more human-readable code.
Unless works really well when there is one named boolean variable after it and it's named correctly or if the condition is simple to understand.
It also works well when used inline and without an else statement. For other cases "if" is better-suited. But then again "unless" is another option in your tool chain that makes for a more elegant programming language if you're not a stickler and not constantly finding yourself pining for a platonic language.
user = User.find(2000)
return unless user
It'd be easier to miss the "!" character in the case of "if !".It also makes code read better in English.
My guess is what's putting me off on unless is the baked in negative. If I look at a condition and imagine "if true" I can follow along, but "unless true" makes me think "what does that even mean". Another issue might be that not everyone is a native english speaker (like me), and that we underestimate that thinking in boolean logic might not be the most natural thing in the world, and once you learn it, you usually learn it in a foreign language. Substituting "if !" with "unless" just throws a wrench in the patterns you're used to using.
I disliked it as well in CoffeeScript. The use of else blocks is especially annoying, but even otherwise I don't like it. Some might say you shouldn't use it with an else block, but this is not realistic if you work in a team.
I disliked it as well in CoffeeScript. The use of else blocks is especially annoying, but even otherwise I don't like it. Some might say you shouldn't use it with an else block, but this is not realistic if you work in a team.
statement unless condition is a wonderful bit of syntactic sugar. It reads like natural English if the variable or method it is evaluating is named well.
Yes, sometimes you have to refactor into a traditional if. Sometimes you make the conditional its own method (which you’d end up doing anyway, assuming it didn’t stop at two or three conditions.) These are light tasks as the project grows.
Starting a line with unless, on the other hand, does not work for me. It is also awkward in short English sentences. And long English sentences, where beginning with unless is more appropriate, don’t have analogies in single-line ruby statements.
Yes, sometimes you have to refactor into a traditional if. Sometimes you make the conditional its own method (which you’d end up doing anyway, assuming it didn’t stop at two or three conditions.) These are light tasks as the project grows.
Starting a line with unless, on the other hand, does not work for me. It is also awkward in short English sentences. And long English sentences, where beginning with unless is more appropriate, don’t have analogies in single-line ruby statements.
I'm mildly surprised the article doesn't mention that Perl, one of the languages that influenced Ruby, also has unless.
I don’t disagree with the essay, and I do generally dislike “unless” as it’s a cutesy statement which does not pull its weight (it would probably be better if there was no “else” clause at all).
However I find some of the examples / justifications unfortunate e.g.
> I find the second option less readable because it suggests that raising the error would be the normal thing to do, when in fact it’s the exceptional thing to do.
It’s not tho. The normal thing to do is to reject access, being an admin is exceptional. A restricted endpoint should absolutely assume the user is not authorised.
However I find some of the examples / justifications unfortunate e.g.
> I find the second option less readable because it suggests that raising the error would be the normal thing to do, when in fact it’s the exceptional thing to do.
It’s not tho. The normal thing to do is to reject access, being an admin is exceptional. A restricted endpoint should absolutely assume the user is not authorised.
I liked "unless" in Perl and also the
X if Y
X unless Y
syntax. I want to see some language that brings back the "noise words" from COBOL for that matter.In Common Lisp, there is `unless' and `when' but they don't allow `else' branch.
(when CONDITION BODY)
(unless CONDITION BODY)
The value of the statement is BODY or NIL if it wasn't executed.
I can't remember a time when it wasn't clear (though you can always build usage that are unclear).
(when CONDITION BODY)
(unless CONDITION BODY)
The value of the statement is BODY or NIL if it wasn't executed.
I can't remember a time when it wasn't clear (though you can always build usage that are unclear).
Alas, it seems I've missed my chance to discuss one of my favorite things: Ruby. If you're a latecomer to this submission like myself, here are three things to keep in mind as you read the replies:
- Many remarks concern the difficulty of understanding "unless" for those with a first language other than English. It may be interesting to note that Ruby was created in Japan, and that "unless" has been in the language since the very first release. [1]
- Matz has a very interesting and famous quote about the principle of least surprise: "it means the principle of least surprise after you learn Ruby very well." [2]
- Computers don't care how programs are structured. Businesses don't care how systems are architected. The canvas does not care how paint is applied. Software engineering is an art. [3]
---
1. You can verify this yourself - check `parse.y`: https://web.archive.org/web/20071109044522/http://eigenclass...
2. The rest of the quote is also wonderful: https://www.artima.com/articles/the-philosophy-of-ruby#part4
3. The article that contains this line has fundamentally altered how I view our profession: https://www.neversaw.us/2022/01/30/the-canvas-cares-not/
- Many remarks concern the difficulty of understanding "unless" for those with a first language other than English. It may be interesting to note that Ruby was created in Japan, and that "unless" has been in the language since the very first release. [1]
- Matz has a very interesting and famous quote about the principle of least surprise: "it means the principle of least surprise after you learn Ruby very well." [2]
- Computers don't care how programs are structured. Businesses don't care how systems are architected. The canvas does not care how paint is applied. Software engineering is an art. [3]
---
1. You can verify this yourself - check `parse.y`: https://web.archive.org/web/20071109044522/http://eigenclass...
2. The rest of the quote is also wonderful: https://www.artima.com/articles/the-philosophy-of-ruby#part4
3. The article that contains this line has fundamentally altered how I view our profession: https://www.neversaw.us/2022/01/30/the-canvas-cares-not/
Ruby is an interface between at least three realms: the human (typically English conversant), the machine (instruction-based), and logic.
Not unique and not the best, but popular and usable enough to be fascinating for anyone who's given more than passing thought to how difficult it is to communicate in any one realm separately (let alone across realms).
For example, I had a meeting with POs recently where it took an hour to communicate a concept in English and distill a logic diagram from it. I'm not placing blame on English (although some would [1]), POs, or myself. It's just common experience that communication is difficult.
Personally I always enjoy flexibility and expressiveness when writing (English or code), usually when reading (English or code), and sometimes when debugging (English or code). So I like Ruby pretty well. YMMV.
[1] https://news.ycombinator.com/item?id=33513666 & https://news.ycombinator.com/item?id=22689959
Not unique and not the best, but popular and usable enough to be fascinating for anyone who's given more than passing thought to how difficult it is to communicate in any one realm separately (let alone across realms).
For example, I had a meeting with POs recently where it took an hour to communicate a concept in English and distill a logic diagram from it. I'm not placing blame on English (although some would [1]), POs, or myself. It's just common experience that communication is difficult.
Personally I always enjoy flexibility and expressiveness when writing (English or code), usually when reading (English or code), and sometimes when debugging (English or code). So I like Ruby pretty well. YMMV.
[1] https://news.ycombinator.com/item?id=33513666 & https://news.ycombinator.com/item?id=22689959
Unless and ternaries are great signals in a pull request that you should build a truth table and double check it. They're backwards all the time, especially in less tested, error handling codepaths.
I've written ruby everyday for the past 5 years. I still cannot read an unless statement and understand it first time. Most of the time i'm translating it to `if !` anyway.
Ruby is not the only language with "unless", actually. For example it exists in GNU Guile as well (https://www.gnu.org/software/guile/manual/html_node/Conditio...). However, due to the usual structure of functions and the last expression being the return value, one always expects a return value (why else call the function?). So "when" and "unless" are only seen inside procedures, which have side effects.
I almost never write unless, unless it makes absolute sense in English, and it only has one condition
Unless seems to be pretty common in many languages. https://www.indifferentlanguages.com/words/unless In my mind the javascript community would do well to address the idiosyncrasies in its language standard before criticizing other languages for using common human language patterns. If you want to pick on ruby for being weird with conditionals, consider the following.
> As a general rule I think that if a language has some feature for which there is already a commonly understood syntax across other languages, it should just use that syntax. If you’re introducing a complete paradigm shift, then that’s fine, but unless is not that: it’s just a different way to write if ! and people jumping back and forth between ruby and, say, javascript, now have one extra idiosyncracy to keep in mind.
if 0
puts 'true'
end
which will print true. I think there is a much greater chance that 0 being true will cause problems with programmers from other languages than using unless which is quite natural to humans. Ruby is first and foremost a language for humanity, and probably dead last a language for ease of implementation.> As a general rule I think that if a language has some feature for which there is already a commonly understood syntax across other languages, it should just use that syntax. If you’re introducing a complete paradigm shift, then that’s fine, but unless is not that: it’s just a different way to write if ! and people jumping back and forth between ruby and, say, javascript, now have one extra idiosyncracy to keep in mind.
This is a useful feature to have when writing one liners e.g.
% cat words
all
these
worlds
are
yours
Which I believe is one of the reasons Perl allows it: % perl -ne 'print unless /^a/' words
these
worlds
yours
And Ruby does too: % ruby -ne 'print unless /^a/' words
these
worlds
yoursIt’s a bit awkward to grasp initially but personally I like it. I don’t wish it in all languages but it works in ruby. It especially works well in templates and checking collections.
Honestly, I find negative checks like `if !something` ugly, I would much rather check if the result is true than !true. The same with unless, I would prefer to check unless false than unless not false or unless !false
Honestly, I find negative checks like `if !something` ugly, I would much rather check if the result is true than !true. The same with unless, I would prefer to check unless false than unless not false or unless !false
on a project about a decade ago I had written some ruby code in a non-rubyish manner (non-rubic?) in that I had written if statements.
I had actually decided to write them as if statements because that way I didn't have to make one particular logical check (I can't really remember what the logic was, just that it had to do with language and Swedish language legal documents did not have a particular rule that Danish ones did.
So a senior Ruby dev who admittedly was a lot better than me in a lot of things but did have the habit of making incredibly boneheaded bugs sometimes, went through and rewrote all my if statements as unless statements - inadvertently reversing the logic.
A week later bug comes up I get blamed because it was the thing I worked on I was embarrassed, and I went through the code fixing but then I thought - wait I wouldn't have written this as an unless because I need to do one extra statement that way! After digging through I caught our senior dev, the very guy blaming me for causing bugs.
Ah the revenges of youth are very sweet.
I had actually decided to write them as if statements because that way I didn't have to make one particular logical check (I can't really remember what the logic was, just that it had to do with language and Swedish language legal documents did not have a particular rule that Danish ones did.
So a senior Ruby dev who admittedly was a lot better than me in a lot of things but did have the habit of making incredibly boneheaded bugs sometimes, went through and rewrote all my if statements as unless statements - inadvertently reversing the logic.
A week later bug comes up I get blamed because it was the thing I worked on I was embarrassed, and I went through the code fixing but then I thought - wait I wouldn't have written this as an unless because I need to do one extra statement that way! After digging through I caught our senior dev, the very guy blaming me for causing bugs.
Ah the revenges of youth are very sweet.
That's a lot of words for a simple matter of cleanliness. Do not use 'unless' for complex expressions, only single conditions. I personally hate grouped negatives like the one shown:
!(valid_token && !expired)
This expression is also a double (triple?) negative, and a lot harder to parse. The correct approach is to make the `valid_token` flag take `expired` into account, or add a third variable that represents validity, not append `expired` to the unless clause. This keeps everything readable, if the variable names are descriptive enough: valid_token = token.valid? && !expired
return 'invalid' unless valid_token
It's also damning that the author uses `unless ... else` as his starting point for the critique, while he is aware that the style guide says "Do not use unless with else" (briefly acknowledged in the last section).
Why? Because it allows you to express yourself more elegantly.
The click-bait title is misleading. It's meant to ridicule "unless", but actually achieves the opposite.
If you were to write the title of the post as code, it would be:
That would be a terrible use of "unless"! That should clearly say "if ruby_dev", not "unless !ruby_dev".
But what if you wanted to write an article meant for anyone other than ruby developers?
Which of the following is better?
or
Both work, but I consider the second option more elegant. Just as I wouldn't verbally say "if you're not a ruby developer" rather than "unless you're a ruby developer".
Honestly, I don't see the issue. It's a style matter. Just use it properly. All language can be abused if you try hard enough.