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.
And Perl literally has the opposite design principle, TIMTOWTDI or "there is more than one way to do it".
I'm not arguing here -- I prefer the "one obvious way" principle.
But you can design a beautiful programming language without regard for the long-term learnings of software engineering.
I always liked Perl's "unless", and I always made sure to not abuse it with double negatives or other contorted conditions that are, in themselves, reasonable. I also promised myself I wouldn't write very big programs in Perl.
I came to Ruby from Perl, and I love `unless`. One of the first things I do in any Lisp is build my own unless macro unless the dialect already has it :)
die "You may only use port numbers 1024 and higher"
unless $port >= 1024 || is_root(current_uid());
die "Port numbers 1024 are forbidden"
if $port < 1024 && !is_root(current_uid());
> Everyone has an individual background. Someone may come from Python, someone else may come from Perl, and they may be surprised by different aspects of the language. Then they come up to me and say, 'I was surprised by this feature of the language, so Ruby violates the principle of least surprise.' Wait. Wait. The principle of least surprise is not for you only. The principle of least surprise means principle of least my surprise. And it means the principle of least surprise after you learn Ruby very well. For example, I was a C++ programmer before I started designing Ruby. I programmed in C++ exclusively for two or three years. And after two years of C++ programming, it still surprises me.
And I love it for exactly this kind of design. It's what makes well-written Ruby read well. And while it's one more way of making badly-written Ruby read badly, in my opinion at least with Ruby you have the option of writing code that reads well. I've yet to see Python code that looks readable to me.
It's not pointless, it makes code read better. It's another option to have. It's not meant for every use case. It indicates you don't understand Ruby if you find yourself using an "unless" in a complex boolean operation. It's meant for simple cases like an inline return statement
reads just fine and takes less letters. I don't think unless is "bad", it's just unnecessary
> It indicates you don't understand Ruby if you find yourself using an "unless" in a complex boolean operation. It's meant for simple cases like an inline return statement
unless `git status -s | grep -v 'RAILS_VERSION\\|CHANGELOG\\|Gemfile.lock\\|package.json\\|version.rb\\|tasks/release.rb'`.strip.empty?
abort "[ABORTING] `git status` reports a dirty tree. Make sure all changes are committed"
end
is not exactly very readable and this
unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
options[:default] = nil
end
end
isn't glancable either. Just random snippets from Rails code. You can see how author wanted some bonus points and used unless, if, and ! too. Sure it isn't hard to figure out but it makes it needlesly obtuse
God that's such a landmine when reading code. Seeing a return without an explicit change of scope... Why ? To save one line ?
Yep that's why I hate ruby - worked on one mature codebase for a year and after seeing various such gems used across the project - from >10 devs - I'm confident I will never touch the language again.
works well enough in other languages and shows actual condition (the important part) to programmer first. if that if and extra brackets is really too long Perl way is also option.
If you're returning who cares about the scope? Are you saying visually you'd like to have indentation inside the if body? If that's the case there is also nothing stopping you in most languages from doing something as nasty as:
I do because return means end of scope. Except in your example it hides the fact that it's creating it's own scope.
I'm not arguing other languages can't produce bad code, just that ruby is particularly suited for it especially as number of developers working on code increases. I've seen people propose a linter to enforce consistency - but at that point I might as well chose a language with better design choices - plenty of alternatives these days.
> I've seen people propose a linter to enforce consistency - but at that point I might as well chose a language with better design choices - plenty of alternatives these days.
You're seriously proposing that adding a linter has the same organizational costs as changing languages entirely. Meanwhile, back in the real world...
Ruby has major advantages over many other languages and a linter is basic tooling you should have in every development environment.
Those are called Guard clauses and they are implement in plenty of systems.. this paradigm has nothing to do with "Ruby".
In any case, the Ruby community already has good guidelines on the "Unless" usage, there are few scenarios where they are useful but it's not like you find them everywhere in a codebase.
For example, we don't use "Unless" with "Else", or use Unless with negation (like the article), or use Unless in nested If statement, etc. Rubocop will catch many of these and warn you.
My point is that experienced engineers will use the language as it was intended to and not abuse its features.
I'll admit I'm not a Ruby developer, but wow it allows return conditionally inside an expression? What were the language designers thinking?! I can't think of any other language that allows return inside an expression (or break/continue). Let's see: C, C++, C#, Python, Javascript, Object Pascal, Java, Rust; all nope. That is indefensible.
You've listed languages that separate statements from expressions in their syntax. There is a whole other family of languages where choose consists entirely of expressions - starting with LISP in the 50s, and including virtually all FP languages today (F#, Ocaml, Haskell, SML, Clojure). Ruby happens to be one a member of the latter group. To be fair, most of the languages in that group don't have an equivalent of return/break/continue either - I think only Ruby and some Lisps do.
Regardless, all of the languages above except C and maybe Object Pascal can still have control stop in the middle of an expression, since an expression can throw an Exception (indirectly). Returning is not significantly different from that.
> I can't think of any other language that allows return inside an expression ... Rust
Er, what?
You understand almost everything in Rust is an expression right?
let x = if yeah_nah() { return 5; } else { "X" };
That return is an expression, obviously we mostly care about the expression's side effect which is a change of control flow to leave the function with some sort of integer 5 as the return value (the function signature tells Rust which kind of integer this 5 is) - but the expression "return 5" itself does have a type, it's ! aka Never, an Empty Type because no values of this type can exist - because the side effect will change the control flow before we need a value.
Rust's type inference engine is fine with this because under type arithmetic all the values fit in a single type, the type of "X" - &'static str - there are no values on the left to disrupt that.
It's not doing "return conditionally inside an expression". The above statement is equivalent to:
if !condition
return
end
In other words, the return in question has no argument. The unusual (but not unique) aspect of Ruby here is that if/unless can suffix the "then" block and not just prefix it.
This isn't exactly true. It's a syntax error to put a return anywhere where the grammar expects a value expression, because it's one of few constructs in Ruby that doesn't return a value (contrast with e.g. "if" and "def" and "class", all of which return a value).
E.g. "42 == (return true)" is a syntax error while "42 == if false; true; end" is syntactically valid.
You can return in the expression in the comment above because the return is at statement level - nothing above it in the grammar requires a value (but obviously allows it).
Off the top of my head I can't remember which other keywords fall in that category. Obviously "end", "then", "alias" and there'll be a few more, but for most keywords in Ruby you're right they can be treated as expressions.
I love Ruby for this kind of stuff. You don't have to have arguments about which syntax you prefer, you can just use the one you prefer and let your coworkers use theirs.
However, a lot of developers love very strict style guidelines. I've never understood why, but it's a discussion I get into very very often.
Hm, I have the exact opposite interpretation. This is exactly the kind of feature that leads to arguments. If `unless` didn't exist, what argument could people be having about using `if !`?
In a shared codebase, I think there should be as few (equally effective) ways to express an idea as possible. (Obviously there can be more- and less-efficient ways to write a function; I'm referring only to style.) The more arbitrary choices people have, the more time is wasted on choosing one.
If you're coding alone, that's fine - you just pick what you like, and spend as long as you like making that decision. But in a shared codebase, developers writing in different styles is a net negative on quality & readability. Spending time debating which style to prefer is a worthwhile, but unnecessary time sink. Automatically enforcing a pre-defined style is efficient and effective - that's why Prettier exists, is widely used, and has few configuration options.
> In a shared codebase, I think there should be as few (equally effective) ways to express an idea as possible...The more arbitrary choices people have, the more time is wasted on choosing one.
I highly disagree! In my experience, forcing arbitrary style leads to so much more time wasted as people debate exactly which styles to enforce.
> But in a shared codebase, developers writing in different styles is a net negative on quality & readability. Spending time debating which style to prefer is a worthwhile, but unnecessary time sink
I disagree with this too. I have never found this type of style to be something that actually impacts readability and quality. If you using `if !` and I using `unless` is the problem with the codebase, that's not so bad.
I have to say, this is why I'm a huge fan of Python's black. It lets me write however I want, while you get to read my code in a consistent style.
Fair enough, I understand every team is different.
> forcing arbitrary style leads to so much more time wasted as people debate exactly which styles to enforce
If this is happening often, I would unambiguously call that a problem, though. It should only happen once (and then, when enough has changed in the frameworks, maybe once again). This is exactly the philosophy Prettier takes: it is purposely difficult to change. It works really well as it is, and keeping it constantly optimized for community preference would be an anti-pattern.
> I disagree with this too. I have never found this type of style to be something that actually impacts readability and quality. If you using `if !` and I using `unless` is the problem with the codebase, that's not so bad.
I more or less actually agree with you on this. Small stylistic differences are generally negligible. But variations add up, and some are more egregious than others. I consider this more of a guiding philosophy than a rule; `if!` vs `unless` on its own is not a real problem.
The problem is that what is obvious depends upon your way of thinking. To me very little in Python makes sense, but I understand that it does to someone and that’s cool. There’s no reason to hate on a language just because it doesn’t click with your way of thinking.
People can prefer the "one obvious way" and you can use a language that has that as its motto. Ruby was built with "developer happiness" as an objective and I don't see it changing any time soon.
I'll never understand these sorts of articles because they criticize the subject for not being what they explicitly aren't.
"I hate spoons because they can't cut like knives"
Wow, I didn't realize Python did this. So, for example, does it enforce functions with single returns or ones with multiple return sites? Also, does it prefer shallowly nested functions with guard clauses or highly nested if / else clauses?
Really curious to see the one and only one obvious way these are handled.
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.