Hacker News new | past | comments | ask | show | jobs | submit login
Read this post ‘unless’ you’re not a Ruby developer (jesseduffield.com)
230 points by genericlemon24 on Dec 13, 2022 | hide | past | favorite | 311 comments



I'd consider myself a writer more than a developer, but I've been working in Rails for over 15 years, and one of my absolute favourite things is "unless".

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:

  unless !ruby_dev
    read article
  end
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?

  if !ruby_dev
or

  unless ruby_dev
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.


There’s a cost-benefit analysis here that you are refusing to do: Of the two variants you’ve posted, the first will click immediately with a generalist dev who doesn’t know Ruby. The second will have them reasoning out loud, and then reaching for the docs to double check that their common-sense intuitions are correct.

This is not a contrived example. I regularly find myself having to read some code in a language in which I am not proficient. Stuff like ’unless’ is such a pointless obstacle in these scenarios.

So ”elegance” doesn’t come free. The cost is a loss of transferable knowledge from other languages. Your intuition is that this cost is low relative to the benefit of ”elegance,” I have the opposite intuition. Why stray from such universal conventions in favour of one person’s subjective aesthetic preference?


Personally, I don't think "readable by people that don't know the language" is a reasonable feature to optimize a language around.

And if you go on that direction, almost the everything on the language is a larger roadblock than an oddly placed conditional.


I never suggested we ”optimize the language around this feature.” I only suggested that it is a higher virtue than some user on HN’s subjective concept of ”elegance.”

What do you think is more important: the speed at which C programmers can navigate your code base, or one person’s subjective idea of ”elegance”?


"Elegance" can mean many things, some irrelevant, some very important. But the speed at which C programmers can navigate some non-C code is absolutely irrelevant.

Specifically about Ruby, with it's infinite levels of metaprograming and "you can even redefine the meaning of blank space" philosophy, it's not 'unless' that will stop anybody.


I wrote Ruby for years (for fun). I always had to stop and think twice about 'unless' and translate it into 'if not'.


Exactly the same here. I write Ruby for a living and use 'unless' where it makes sense or fits with existing code.

That said, when reading code, I often have to pause and mentally translate "unless" to "if not". This is especially true for more complex logic.

Elegant, yes. Additional mental overhead at times, certainly.


It is not all about cost-benefit.

You say there is a difficult time transferring knowledge from other languages when they see the `unless`. That's fair.

My response to that is that, to idiomatically think in Ruby is write code that reads well, and `unless` fits that. Every language has an idiomatic way in which one thinks and reasons with the language. While there are principles that can be transferred over across languages, the way to think in that language does not necessarily transfer. The Ruby community has a heavy emphasis on creating embedded DSLs that seems natural enough to written English, and the Ruby community's style guide is written for that.

In contrast to Ruby, Python has idioms that seem to be the inverse of Ruby. The intuitions on what is good, idiomatic Ruby is counter-intuitive in Python. This even extends to the design of Ruby bundler vs. Python virtualenv/pip. As a long time Rubyist, Python rubs me the wrong way (what is intuitive in Python is counter-intuitive in Ruby) until I realized that I used to write that style of code a long time ago with Pascal.


I have a long history of Python and it fits my brain well, but the last 7 or so years have been in Ruby shops. I still struggle with trying to write idiomatic Ruby sad to say.

I do like `unless` in Ruby, but only when it is the one liner form and the exception happens pretty rarely. The multi line form often takes a lot of effort to parse for me if it is more than a very simple expression.


Probably the biggest thing is where and what is being encapsulated. Ruby is inspired by Smalltalk. Objects sends messages to each other, as if the objects are autonomous. They can receive a message, but it is within the object’s discretion on how that is interpreted.

Python exposes those methods. What you see is what you get. Classes and objects are not considered as autonomous agents so much as data structures bound with functions that can operate on it.

More controversially than “unless” is Ruby’s way of calling an anonymous function. Almost every other language, if “f” is a function, you can call it with f(). In Ruby, an anonymous function is an object, and you call it by sending a message “.call()”; the shorthand for “call” being “f.()”. Everything is an object.

This trips up even polygots who like Ruby. But if your mind can shift in such a way where that is ever seen intuitive, then you’re probably well on your way to being able to think in Ruby. (Assuming someone _wants_ to think that way).


This is a great comment. The language I use most is Python, and I am definitely biased toward its conventions. It's interesting to hear from the other side of the fence.

I want to suggest a slight reframing: instead of saying: "It is not all about cost-benefit," you could say "here is an addition to the 'benefits' column that hasn't been considered yet."

It's a totally reasonable argument; much better than "elegance." This was my main problem: that the critique of `unless` was much more plausible than the defence.


I agree that thinking in terms of this as additional benefits is an excellent way of thinking about this.

However, we don’t always make decisions purely on cost vs benefit. Some types of decisions, such as strategic thinking, involves unfair advantages. If cost-benefits are reasoned from a finite space in which one can find the optimal choice, unfair advantages cheats that, and operates on a potentially infinite space with imperfect information.

Not that I am saying Ruby or Python are unfair advantages :-) I’m just saying that cost-benefit is not the only way in which one can decide on something, and may not be appropriate for every situation. Being able to think in a language can potentially change how you think, and framing that as a benefit can be problematic.

I explored Ruby because I wanted to explore eloquence, semantics, and pattern languages (in the Christopher Alexander sense). I spent over ten years on it until I had my fill. Now I am exploring concurrency, reliability, uncertainty, self-healing and distributed systems — Elixir and Kubernetes. And while I still use Ruby in some tooling, it doesn’t give me the joy that Elixir does.


That's a really powerful tool. I can force you to slow down an make sure that you the reader focus on one section of code I've written?

A statement that forces you to switch out of inaccurate skiming mode is insanely useful


I get your point, though it's also worth pointing out that it doesn't force anything. Admittedly, syntax highlighting will generally help but it's always possible that a skim simply misses the keyword.

Practically speaking, that's not how I know Ruby to be written. The convention I follow is to use `unless` when there is no `else` case and only when there is no negation in the expression.


unless is one of those things I have to read 2-3 times and it totally bogs me down.


Interesting! I wouldn't want to argue against your experience.

I do wonder if it's an issue of "unless" being misused? Programmers using it just for fun rather than considering whether it's the best choice in context?


I feel like I have read if statements so many times that I have fast pattern matching circuits in my brain for them.

When I come across an `unless`, I can't use them; I have to come back out into "conscious reading" mode, or something like that. Makes me crazy.


I'm a recovering rubyist (actually I still love ruby, just never get to write it anymore). The only times I felt `unless` was truly more readable than `if !something` was when it was used in the conditional suffix form like

    do_something unless already_done?


This is the poster child use case for "unless", IMHO.

Anything else (at least for me) is more difficult to read, it's probably due to being exposed to all thos other languages that do not have that syntactic construct. Using C syntax conditionals feels more natural to me, but again this is all opinionated.


do_something if not already_done?

Problem with so much syntactic sugar is you need to retain/recall the semantics of the vocabulary. As a polyglot programmer who doesn't write much code anymore, it slows me down.


Same here. I can handle chaining conditions and narrowing things down, but once there's a De Morgan's filter on the thing, there's just too much state to juggle because what's in front of my eyes is not a refresher for what I actually need to remember ("ok, it says `n < 0`…(De Morgan filter)…n is positive" is too much IMO).

That said, I feel it's really down to double+ negation being hard to handle. `if` with a single negation is fine; `unless` is automatically a negation, so you're just off to a bad start on that front. I feel like the only viable use is in a case like Rust's `let … else { diverge }` where anything that fails the condition is guaranteed to diverge (typically, return) and I can just ignore it when reading the overall codeflow. But Ruby mixes that up and does `diverge unless …` which puts the diverging part "up front" and "in the way" for such readings.


Man, I feel like you hit the nail on the head here... this is definitely the best way I could think to articulate why `unless` throws me off so much.

Weirdly, Ruby was one of the first languages I learned early in my career, and at the time I had no problem with `unless`. But after years of experience with other languages, I similarly feel that `if` statements now trigger the fast pattern matching circuits in my brain, while `unless` makes me do a double-take and basically translate it into `if not`

At first I thought I was just becoming dumber with age, but I like your explanation better :P


That makes sense!


In Ruby (and Crystal), I tend to use `unless` for guards at the top of the method, or returns. These are typically `return foo unless baz`.

When I write Elixir, `unless` gets awkward in a functional style of programming, and Elixir has guard clauses and pattern matching. I pretty much never use `unless` in Elixir despite using it in Ruby for years.

Sometimes, I'll add extra methods with a negation in the name itself. So for example

  # instead of
  return "invalid" unless valid?
  return []        if empty?

  # I define invalid?() and do
  return "invalid" if invalid?
  return []        if empty?


Perl also had that delayed syntax check style as an option and I HATED IT.

THING if logical condition

Filter First is far better self-documentation: if condition THING


It works well in cases where you have an operation that consists of a certain sequence of statements, where one of the statements has to be omitted in a particular atypical situation. Instead of having interrupt the sequence with an if, you just tag the particular statement with the omitting condition at the end. That’s also when unless tends to be intuitive, because it suggests an atypical condition.


That is usually where I used it too.

When I started writing these as pipeline execution. Then I’ll use a maybe?() instead if the conditional does not need to depend on the result of a previous operation.


That's a matter of what fits better with the structure of how you think internally, and not necessarily that there is an objectively better way for self-documentation.

For example, I can totally see how imperative-first would mess up people with neurodivergant brains.

Tangent -- Ruby takes a lot of inspiration from Perl.


Also as a non-native English speaker, unless always makes me do a double take. It is not a word that naturally translates in my head, but that might just be me.


I think it is also an issue of familiarity with English. Not all developers are fluent in English.


Just reread until you get it.


Admittedly, until (as the negation of while for loops) tends to be more straightforward to grasp than unless.


I have a similar problem with list.filterNot() in Kotlin


You've demonstrated that it is trivially replaced by 'if' and negation. I think that's almost the definition of inelegant.


Only if you definition of elegance is "please express yourself in as convoluted a way as possible, so as to minimize the number of words I need to know."

The truth is there is a balance in all languages. Nearly every word was invented to prevent having to say a string of other words to mean the same thing.

"Unless" is a single word to mean "If not". I consider that elegant.

However, some languages take this too far, in my opinion. German famously has a word for nearly everything. I'm not sure that's elegant, in that it requires learning a far greater number of words.

English has many words that I wouldn't consider elegant, simply because they're uncommon or convoluted. Never use a 10 dollar word when a 5 cent word will do.

So, if, like me and the ruby community, you prefer to optimize for developer happiness, "unless" is an elegant choice.

If you prefer to optimize for peak computing performance, if may be more computationally efficient use "if not".

But, _unless_ you're saying that you never use the word "unless" in everyday language, I think we can agree it's a good word that has good applications. I don't see why that wouldn't be true in programming.


> German famously has a word for nearly everything. I'm not sure that's elegant, in that it requires learning a far greater number of words.

That's not quite how it works though, right? Compound words are exactly that, combining two or more words to narrow down the meaning, without having to invent a new word. It's almost like "if not" vs "unless"...


Correct. The chief difference between English and German in this respect is the use of spaces (and what is considered a word). In German, you can basically drop the spaces from a noun phrase and call it a word, but it's basically a low-consequence surface syntax difference.

Side note: ancient Greek didn't use spaces, and many other languages (Thai and Written Chinese are the ones with which I'm most familiar) either don't use spaces or make spaces optional. The distinction between a phrase and a word gets a bit blurry at times, and I find the distinction is seldom useful, particularly when making comparisons across languages.


Fascinating! I had no idea. I stand by my main point, but perhaps German wasn't a good example. It's just the one that comes up all the time when people say "Did you know X language has a word for Y?", it's almost always German. Now I know why!


Unconscious clarity trumps literary fluidity when reading code.


The best example of vocabulary size and its tradeoffs this is the Guy Steele talk "Growing a Language". One of the best talks ever given.


It's Ruby though, where Array has "size", "length", and "count", all of which are equivalent. The programmer chooses which one they prefer based on the context or their style or the phase of the moon.

Ruby inherits the Perl philosophy of "There's more than one way to do it". Compare with Python, and its "There should be one — and preferably only one — obvious way to do it."

It's true that it makes the compiler less elegant, but Ruby optimizes for the programmer at the expense of the compiler.


Not exactly the same. It's been a while, but if I recall, count takes a predicate block so you can do things like `numbers.count { |n| n.even? }`


It can also take an argument and will then return the count of the number of items which compares equal to the argument.

"count" is only equivalent to "length" and "size" when called with no argument and no block, and should be slower for that case (it includes a comparison of arity and a call to rb_block_given_p() in MRI before it returns the array length).


Equivalent in output but not in implementation / performance. Does that matter? Not usually, no, but it depends.


A for can be replaced by a while and a jump, that does not make it more or less elegant.

IMHO, something ‶elegant″ in programming is not something that can't be built form something else (or we will all end up in writing only CMOVs), but something that conveys the meaning of its author precisely and concisely – which makes it inherently subjective. I'm in the `unless` team, I can perfectly understand if you are in the `no unless` team, but this argument does not make a lot of sense.


Note that I said 'trivially'. The structure and meaning of an 'unless' construction is the same as 'if', adding or removing only a negation. Using 'while' and a jump to construct a loop is much more expressively distant from 'for'.


The `for` is a very ergonomic syntax to mapping a computation to every element of a sequence. The `while` does a really different thing, it's not about sequences at all, but about checking some bit of mutable state repeatedly.


> The `for` is a very ergonomic syntax to mapping a computation to every element of a sequence.

The very ergonomic solution to map a computation to every element of a sequence is `map`. In this case, `for` is filled with bookkeeping that does not matter.


I edited out "syntax sugar" that was in the post initially.

Also, the `for` operator definitely predates the `map` function. Some people also prefer `map` to be more or less pure and expect it to return a usable list; `for ` has no such expectation, it e.g. may consist solely in printing elements.


> Also, the `for` operator definitely predates the `map` function

And `CAS/Compare Accumulator with Storage` definitely predates `if`, still you are most probably using the latter.


One of the hallmarks of Ruby is a full embrace of the notion of There's More Than One Way To Do It. Generations of developers around the world have seen this as being a core part of Ruby's elegance—at least to their minds (myself included).


Negation makes code harder to read and understand.


I'm getting the impression that this is not universally true. It's true for me. I find the word "unless" helpful when it eliminates double negatives.

But there are enough people on this thread who feel differently that it makes me wonder if maybe we're wrong. If code is meant to be read and understood by all, and if "unless" is confusing to a large number of people, maybe those of us who like it should knock it off.

I do find it more elegant and easier to read, but maybe you and I can process double negatives easier than the anti-unless crowd are able to process "unless" logic.

If that's the case, I'd be willing to sacrifice my preference for "unless" for the greater good.


> double negatives

Could you give an example of a double negative in this case?

I ask because using "if" does not lend itself to double negatives in my experience, but just thinking about "unless" I see double negatives being an issue (since unless is already negating the operand).


Maybe he means unless this and|or|xor that. That's what I use it for rather than negate every paraneter.


Sometimes adding a temporary boolean variable is a good way to resolve a double (or single) negative.


People are different, so different things click with how they think.

I would likely not be a programmer if it weren’t for Ruby and it doing things different.

I had tried to learn programming for years (JavaScript, Python, Java, C#), none of it stuck or progressed beyond following tutorials and one of the things I had serious issues with was negation.

Then I came across Ruby and it just kinda clicked into place like no other language had. With Ruby I was able to do my own things within a month.

Now I don’t really have a problem picking up other languages anymore. But there’s others out there right now struggling to learn programming and there should be as many programming languages doing things different as possible to increase the chance that they come across one that is obvious for the way they think.

This idea of there should only be one way of doing things is poison in my opinion.


Unless has negation built in.

Which is why unless is such a hard concept for people to grasp, not just in code, but also in regular English.

At least the "if not" makes the negation explicit, which can then be easily eliminated using several different techniques (early return, swap with the else condition) if the negation is confusing enough.

However, "unless" necessarily includes negation because that's how the word is defined.


> Which is why unless is such a hard concept for people to grasp, not just in code, but also in regular English.

This is the first I've heard about this confusion over the word "unless" in English.

In spoken English I do notice that most often the "unless" comes after a statement. Example: "I'll be there on time, unless the bus is late." That is more clear (to me) than "I'll be there on time, if the bus is not late." Maybe not by coincidence, I tend to like "unless" when it follows the action in in Ruby code, too.


By the same token, using until instead of while and negation would be inelegant, but I think until is less of a stumble block than unless.


Go to the store and get milk, unless we already have some.

Go to the store and get milk, if not we already have some.


In lisp, "unless" is a great way to signal that there will not be a dual branch. Similarly, they have "when" for the positive case only. They also have the advantage of not needing a "progn" segment if you want to do more than 1 thing on the case that they are "true".

I confess at first I thought they were somewhat superfluous, but they are more readable now that I'm used to them. Completely agreed on it being stylistic, mainly.


For me honestly it's one of the (many) things I dislike the most about Ruby.

Sure, I understand the "elegant" aspect, two symbols are shortened to one (although I'd argue that's not that important when it comes to clarity and legibility).

But every time I read "unless" in code it's quite jarring. I have to consciously translate it to "if not", and even then seeing the "unless" keeps tripping me off, perhaps because it's awkward in English to start a sentence out of the blue with "unless".


It's a matter of taste, but I tend to agree that the more features or constructs a language has, no matter how terse or convenient, the more unnecessary complexity is generated by using that language.

The `unless` keyword makes the interpreter slightly more complicated and is another piece of language the human brain needs to recognize. It may seem inconsequential, but grains of sand make a hill, as they say.

My ideal language would leave out pretty much anything that can be achieved with more basic constructs. It would of course have if-else, and leave out `unless`, but there would also be no switch statements or ternaries. Branching must be done with if-else or by looking up a value in a hash. No `for` or `while` loops because a simple `loop` construct with `continue` and `break` statements can do everything that `for` and `while` could do in other languages. No classes or inheritance because they're magical and they can be effectively simulated by the end developer if that's what they really want.

What I think would be really cool is to have a language with a syntax like Ruby but with only the most basic of programming language constructs.


Why bother with loop, continue, and break when you can have a more generic if+goto that can handle all the cases?

You can simulate the loop behaviour if that's what you really want


I've thought about that, but that's closer to the metal than I prefer and I think it's not beneficial for less experienced programmers.

Structured programming is, I believe, a good idea for the vast majority of use cases. Though one can technically use structured programming while using goto, encouraging goto can therefore allow programmers to go down a path that is counterproductive. It's basically the opposite end of the spectrum from OOP where encouragement of rampant objectiveness and inheritance makes programmers write code that is way too complicated.

There's absolutely a place for goto. I once made a "language" (actually YAML) specifically for developing for the Amazon Alexa platform, and it used something similar to goto called "go to scene" and "go to random". For that sort of thing, goto can be much more practical and even easier to reason about than "better" constructs in general purpose high-level programming languages. It's just not something I would consider appropriate for the kind of language I am proposing, though I still ponder on it.


Many language implementations internally translate source into a simplified de-sugared dialect as you describe as an early compilation step. I believe both ghc and rustc currently do this.

Though, I think it's rare to expand a pattern match/switch into an if-else tree, and much more common to expand an if-else into a 2-way pattern match/switch.

If you're really keen on such a language, you could fairly easily implement such a language as a pre-commit hook source code re-formatter so that all of the source in your repository is in your favorite language subset.


I have problems with 'unless' even in plain English text. I'm pretty good at English, I live in the USA, but I was not born here and my native language does not have a word for 'unless' (you have to use 3 words to express that idea!). I love Ruby, but the 'if' variation always get immediately parsed by my brain, while the 'unless' variation requires many seconds of thinking.


How about if you could write except if instead of unless?


Nothing will ever come close to "if not" due to the decades of experience I have reading it everywhere.


Came here to say this. If you don't like it, don't use it, but it sure does seem to make things easier for other developers (Ruby and not-Ruby-but-assigned-to-this project) to understand. I like it.


I treat the 'if' statement and the '!ruby_dev' conditional as separate entities. That allows thinking of the conditional alone in boolean logic terms, instead of having to consider the outside prefix statement too.

Therefore, coming from a computer science background I don't like 'unless'.


I could see that. Maybe it really is because I'm more of a writer than a computer scientist.

(For context: I dropped out of comp sci and took a job as a web dev ~18 years ago. Never looked back. But have ended up learning more about copywriting and marketing than computer science along the way.)


I'm a computer scientist and not much of a writer yet I prefer the use of "unless" over a negated "if". Then again, at the start of my career I used quite a bit of Perl and it was created by a linguist.


Doing Rails for half a year now and usually are pretty good at mental models.

I always have to stop at an unless because the double-negative is really hard to evaluate in the mind IMHO.

IMHO unless leads to unnecessary nesting. In your example, it reads like _most_ people are ruby dev. I try to keep the most-often excecuted flow as flat as possible (happy path) and just try to nest for additional/special cases. Unless just tries to do... different.


The unless is still harder for me to grok.

The if !ruby_dev (read as "if not ruby_dev") is so much easier.


For some reason this one feels right to express with if-not to me too.

The one place I’ll often use unless is things like:

    return unless valid

    raise unless h.key?(k)
I think it might have something to do with:

1. How abundantly clear it is that the condition is a Boolean; and

2. The “false” condition indicating a “no work to do” situation.

I think I only use them for postfix return, break, next, and raise.


Fascinating! I find it slightly easier to understand "unless" than "if not". But only oh-so slightly, and probably not enough to justify making it harder for others to read my code.


I agree with you. I think the complaints come down to "don't give people more confusing ways to write conditionals". And I agree with the complainers that it is easy to write conditionals in a confusing way, and that there are simply more confusing ways to write them when you can also use unless. But I think the problems come from not taking the time to make the conditional easy to parse, which is an issue whether or not you have unless. And it's not just trading if ! for unless (or if for unless !) -- sometimes it's "if valid" vs "unless !valid" vs "if !invalid" vs "unless invalid", depending whether it was easier to write the predicate for valid or for invalid. Even working in a language without unless, I frequently find myself encountering these difficulties, and I find that the only thing that helps is treating it like writing and deciding what logical progression will be the most helpful for the reader. What's most helpful for the reader won't always be to use unless, but sometimes it will, so it's nice to have the option.


“If you are [not]” serves a different function than “unless.”

“Unless” is a conjunction, and its presence creates a double negative with the end of the sentence making the sentence extra clunky (“unless you’re a Ruby dev, you shouldn’t read this article.”)

A few iterations later, we get to the correct syntax, “read this article if you’re a Ruby dev.”


'if !<something>' clicks instantly. for 'unless' I have to read out the statement in my head, and draw mental logic lines about what condition this is checking.


> Which of the following is better?

  if !ruby_dev
or

  unless ruby_dev

As a ruby dev of 5+ years, I still have an easier time with `if !ruby_dev`. I have to translate `unless` to `if !` every single time to grok something. Almost like an extra step in an algebraic expression being simplified.


Different taste I guess. I work in Python and always go out of my way to write "if thing is True" or "if thing is False" nowadays to make conditions clear and uniform.


Should I read the damn article or not? I still have no idea


That's how I see it as well. Also, if you need to tag on more conditions, convert it to an `if`

The `unless` is idiomatic to the Ruby way of thinking.


as a non-native speaker, unless requires additional time to parse than "if not"

Also note that unless itself contains another negative ( < medieval onlesse)

It's just easy to see if more than one conditions are evaluated:

if (!ruby_dev && cpp_dev)

vs

unless (ruby_dev && !cpp_dev)


Nope. It's needless confusion. We're programmers who have stuff to do, and syntactic stuff like unless are not helpful.

if (true) and if not(true) is simple and elegant, and doesn't require any person on my team to stop their flow to puzzle out what the code is supposed to be doing at that point.


But inventing totally new languages even for billion dollar projects is totally fine?

You want a well-justified, inclusive, and consistent yet adaptable style when collaborating with people. It would be selfish to gatekeep a policy without regard for those principles.


"if" is almost always easier to reason with, "unless" can be elegant, but it's so often misused that it doesn't really matter


I agree entirely that it can be misused. I'm not sure how often it's misused versus used properly. But I don't see why I should give up a useful logic tool just because others don't know how to use it well?

Let me put it this way: Are there _any_ programming paradigms that don't get poorly used? We need to learn how to use our tools well, not reduce them to banality.


I used to work at a garage, and while an impact wrench can be the fastest way to put a tire back on a car, I eventually banned it entirely.

Everyone's lazy or overwhelmed at some point, and it made mistakes too easy to make (the problem is that it overtightens the lug nuts because the ease of use removes the proper physical feedback).

In situations where you don't have the bandwidth to perfectly educate everyone and enforce rules, sometimes it's easier to limit the toolset. A tire iron can be misused too, but it's a little harder.


On this note: I’m sad ruby style guides generally ban “and” and “or”.

   raise(“a long error string”) unless valid
This reads much worse, when the string pushes the line near max width, than:

    valid or raise(“..
Of course it’s another idiom to learn but it’s not a difficult one when used simply.


That’s a very Perl idiom, did you previously write Perl?


No, I just think for all ruby is bought into these guard clauses, it's sad that we generally reject the feature that allows putting either the condition or the consequence first, depending on which reads better.


the 2nd form implicitly returns the value of valid if it's truthy and nil if it's falsey; the 1st form always returns nil


Indeed, but the return value of a guard clause shouldn't be important!


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.


That’s exactly how I use ‘unless’. Simple on condition (no else branch) use cases in one liners, anything more complex, I move to if. Best of both world I’d say!


That’s the generally recommended style! I hate this post. It’s arguing against something that is well known to be a bad practice (that’s fine), but it seems to put the fault for this on Ruby itself.


Yes! And rubocop can encourage the use of 'unless' in only these simple situations where it does enhance legibility.

https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/Unle...

https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/Unle...


Indeed. I personally never fail to avoid not using "unless", unless there isn't a lack of extra conditions such that not refraining from instead using "if" wouldn't avoid not being less clear.


I'm not sure that making this sentence as obtuse as possible is a great idea to convey your point.


Making it as obtuse as possible was very likely the point.


SURGEON GENERAL'S WARNING: reading this sentence before consuming caffeine may cause significant brain damage


The author's point is that conditions in code tend to add up.

What may start as a simple single condition, may not remain that way a year later.

Since there's always the possibility of a certain conditional becoming more complicated, it makes sense to opt for the equivalent solution that makes adding those additional conditions easier.

The alternative is to change the keyword when you add those additional conditions, but in most workplaces it's very possible that someone with less rigor than you might come in and add those new conditions and not make the change.

And then if you want to add a new condition you need to parse and understand the more complicated new conditional first, before you can change it.

This adds a lot of maintainability risk for what appears to be, in the best case, a very minor benefit.


That strikes me as a premature optimization. If you have a single condition, and unless makes it more readable, use unless. If the conditions do pile up in the future, change it to an if.


Great, now you've got this weird logic on your style guide that your code reviewers will have to point to, and hopefully the new engineers on your team will remember. But maybe they're in a hurry and will quickly add another expression just this one time, multiplied by many engineers.

It's far simpler to ban use of unless.


It is neither weird, nor complex. Use unless in cases where the if has a single, negative condition.


While I agree with the other comment re premature optimization, when using guard clauses you can also break up your conditions into multiple statements which is arguably the most readable solution. The author’s example could be written as:

  return unless valid_token
  return if expired?
This scales extremely well if you’re concerned about future conditions.


I believe this is somewhat prevalent in the Ruby community as of late where people try as hard as possible to "lock down" the language and limit the ways you can get to a solution in the name of the proverbial "ease of understanding".

I wholeheartedly disagree and believe we should instead grow the developers to understand these different approaches as opposed to labeling half of the language "bad practice".

As I said in another comment. Ruby was built to have multiple ways of doing the same tasks and criticizing it for this is... pointless.

Yes people can create monstrosities with all of these variations, but that's true with any powerful tool. If you dislike Ruby for being Ruby, pick another language.


I love an expressive programming language, it’s why Go makes me feel like I’m brushing my teeth with sandpaper…

But there’s such a thing as too much flexibility, expressiveness and flexibility often go hand in hand as flexible language lets you make things as expressive as you want… but things can be expressive without the same level of flexibility if they are well designed.

I find Rust strikes a good balance for low level language work, it feels more expressive than C because of higher level language features but it’s not necessarily as flexible as C with things like the borrow checker nagging you to be safe unless you turn it off.

Lisp… too flexible, too expressive. Lisp code reminds me of the phrase “that depends on what the definition of is, is”.

Scheme and scheme likes including weird step cousins like JavaScript share the unfortunate result of this and your drowning in custom DSLs and no one quite codes the same way unless you have rigorous tooling to enforce a team style.

Ruby falls foul of this for me too. It’s too flexible, it’s very expressive and I enjoyed reading the tutorials and learning the basics. But the moment I discovered real code I was immediately recoiling in horror …. It was a disaster… a spaghetti mess of overrides redefining and meta-programming in general. It made it impossible for me to ever trust the environment I coded it since I had to constantly inspect everything to make sure someone hadn’t done something crazy like enhanced the “+” sign to transform numbers to strings for avoiding something like a sql injection risk in a specific context and whoopsie, leaked the behaviour globally so now any time you added integers you got concatenated strings… but only after the library was dynamically loaded on first query … maddening stuff…

Stuff like this is why ruby is too flexible.


I do not program in Ruby as there was no business need but I have rough idea on what it can do. My view on the horror you've encountered is that the so called "programmers" that produce such abominations should be sent back to school.

I understand business idea of having monkeys producing code for cheap but I think this approach is misguided.


> we should instead grow the developers to understand these different approaches as opposed to labeling half of the language "bad practice".

> As I said in another comment. Ruby was built to have multiple ways of doing the same tasks and criticizing it for this is... pointless.

Agreed 100% with this take. If I only wanted 1 way to accomplish something no matter how inelegant it might be in that context, I would use Python. I choose Ruby specifically because of how expressive it is. It makes it a bit tougher when working in a team with 2 or more opinionated devs, ie. "should we use more functional or more OO approaches to solve a problem" but this is a culture and communication issue, not a language problem. Having a good rubocop setup and staying within the confines of your agreed upon ruleset is a good start.

For example you can use rubocop to enforce dissallowing `unless !condition`, then it's just not an issue anymore - these kinds of issues dissapear before code even makes it to review.


>"As I said in another comment. Ruby was built to have multiple ways of doing the same tasks and criticizing it for this is... pointless. Yes people can create monstrosities with all of these variations, but that's true with any powerful tool. If you dislike Ruby for being Ruby, pick another language."

Exactly the reasons I use C++. Well, after crazy performance, efficiency and single executable deployment.


I like C++ because I find angle brackets to be harmonious to the eye.


> “You should use a suboptimal solution to cater for unknown future requirements” is a terrible argument

Not exactly the same, but over the years I've heard people argue against having super customized shell configurations (e.g. completions, prompts, highlighting) because they won't be available if you have to use a different environment (e.g. sshing into a temporary cloud server to debug something failing in CI). I don't pretend that other people will value tradeoffs the same as me, but it's a mindset I can't really imagine ever having. I'd rather be happy most of the time even if I know I'm going to be unhappy for short periods occasionally in the future, and I don't really think the slight efficiency gain in the uncommon case due to being used to not having nice features is going to outweigh the larger efficiency gain for the much more common case.

At the risk of straying entirely off-topic, I've seen this in non-professional contexts at times as well; I recently had a friend in an online game mention that he doesn't like to utilize a convenience feature that happens for a couple weeks each year as part of a special even because he would miss it too much the rest of the time. While I don't think the tradeoff is quite as obvious to me for this sort of thing, I feel like occasionally having a fun temporary addition is worth it just for the change of pace, and I'm generally able to adapt to the loss of a minor convenience relatively quickly after losing it. It seems sort of like a question of "maximizing peak happiness" versus "minimizing peak unhappiness" or even "maximizing average happiness"; I like having something to get excited about every now and then even if it leads to feeling blase about things for a bit later because I get bored if things stay the same for too long.


As an analogy to your shell customization thing:

I switched from QWERTY to Dvorak a long time ago, a few years after learning to touch type. Does it make me more efficient on my own computer? Probably slightly -- Dvorak really is a well-designed keyboard layout, but it's impossible to know whether my WPM is any higher than it would've been on QWERTY. However, I make way more mistakes and type way more slowly whenever I sit down at a QWERTY keyboard. I had to re-learn how to touchtype QWERTY after switching to Dvorak, and my skill never got back to the same level. It also leads to extra inconvenience every time I lend my computer to someone else, install a new OS, etc.

I think this means my average typing speed (weighted average of the time spent at Dvorak keyboards + QWERTY keyboards) is probably flat or possibly lower than if I had just stuck with QWERTY. When combined with all the effort I spent learning Dvorak, re-learning QWERTY, and making many mistakes in the early years, I think my lifetime productivity is probably even lower. I don't recommend that people switch, even if I think Dvorak really is a better layout.


To be fair, he makes the point that

> there’s this bizarre quirk of human psychology where developers retain the unless against all odds

I have refactored a number of unwieldy `unless` statements written by colleagues so maybe there’s something to that.


Exactly my first thought too.


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:

    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.


Maybe these dangling conditionals are easier for you to read, but not for me.

Were I reading through real-world code that did things like that:

"OK, then we do this, and then we do that, and then-- Oh wait! Backtrack! We didn't do that thing at all! ... Now where were we, on our actual bug, before some very special person's syntactic speed bumps?"


But these are not absolutes. You can learn to use and read `unless` just as effortlessly as you did with `if`. I didn't learn to use/read `unless` on college. But I did focus on trying to learn these things and today I have another tool in my belt.

It's an opportunity to get better at something versus banning it from usage because we aren't used to it.

Growing is, for me, almost always the right choice.


Dangling conditionals the parent was talking about. Where the conditional comes after the thing you told it to do. Computationally, that's increased backtrack/lookahead for no good reason.

Regarding `unless`, that's a different matter. I was actually the person who proposed adding an `unless` syntax to Racket, and later regretted it. I was bikeshedding, before I knew something more useful to do.

This isn't about learning new things to add to our mental toolbox. This is about our difficulty to introspect on our own thought processes and effectiveness, and about bikeshedding.

Software engineering competence is hard, or we wouldn't have all the huge expenditures, slipped project schedules, and rampant security vulnerabilities. Redundant syntax forms that objectively makes it harder to read (see the lookahead and parser theory), solely because someone imagined it was sometimes easier to read this way (see dying languages Perl and Ruby) is just wasting our time, when we could be tackling the real problems.

(Though us pulling theories about programming and process out of our posteriors, to defend to the death, is arguably half our problem in industry right now.)


The impetus to write the article seems to be Rubocop, whose rules are suggesting a specific way of doing things. But specific instances of a specific rule can be overridden, and if a rule proves itself to be more trouble than it's worth it can be disabled entirely.

I would go so far as to suggest jumping into Rubocop "cold turkey", meaning all rules on, is not very helpful on an existing codebase. The way my team approached Rubocop on a particular large legacy codebase was to start with a few hand picked rules that were non-controversially good, and discuss new rules one at a time as we gradually fixed the existing issues.


I agree with this - I always found the single ! in conditionals to be too easy for your eyes to slide past and miss. Using `unless` makes it jump out harder that you're only doing the thing if the condition is `false`.


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.


This is basically how I use unless too. Complex conditions are complicated enough to grasp without inflicting an unless on me. But for simple cases it is a nice to have feature.

Of course one can avoid it at all but of course one cannot avoid to spend time on it when reading somebody's else code.


FWIW HN does not support markdown lists (or markdown anything other than emphasis and code blocks really), you need to add an empty line between your “list items” so the comment parser treats them as separate paragraphs instead of munging them together as just one.


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.


To be fair, Ruby inherited "unless" from Perl.

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 :)


I only know `unless` from Perl, and it can make your code clearer if used sparingly. For example it allows you to express preconditions positively:

    die "You may only use port numbers 1024 and higher"
        unless $port >= 1024;


    die "Port numbers below 1024 are forbidden"
        if $port < 1024;
is hardly unreadable spaghetti code.


Okay. Now compare:

    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());


My head hurts! Does unless apply to both branches or just one. The if example is easier for sure.


> 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.

- Matz, c.a 2003


Ruby is basically Perl++


ruby is OOPerl


Ruby is Perl 6


In Perl I once saw someone write;

  unless predicate
    do this
  else unless impossible_thing
    do the impossible
What it did at runtime I have no idea, but it broke my brain for the rest of the week.


    if !cond1
        do this
    elsif !cond2
        do that
?

it's literally just `!` equivalent. Honestly I haven't seen much code using unless in wild. Especially that it is literally shorter to just !


there is no else unless (or even elsunless, since this isn't javascript) in Perl, might have been

    unless (predicate) {
       do_this;
    } else {
       do_the_impossible unless impossible_thing;
    }


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.


Y'know, I've written a lot of python these past years, and python is just full of aliases and different ways to do things.

Just an example: "if x is not None:" is syntactic sugar for "if not x is None:".

String formatting: there are f-strings, % replacement, .format, probably more methods.

And that's fine, languages evolve over time, add better ways to do things, and don't deprecate the old way.

It's the hypocrisy of flaunting that motto while the language is so full of the exact opposite that always annoys 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

  return unless User.exists(id=100)


    return if !User.exists(id=100)
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


Flowers are not necessary either.


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.


Those one line returns are particularly handy at the beginning of methods, to validate or process arguments and return immediately for simple cases.


Short circuiting is such a beautiful way of avoiding giant nested "if" mountains. I wish it was more widely used.


right but

    if condition { return } 
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.

    condition || return


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:

  a=1;b=2;c=3;d=a+b==c?4:5;return d


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.


It doesn't create no scope. Even for variables.


Ruby doesn't create a new scope for if/unless.


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.


If and unless can be used as post-fix clauses. Also, in ruby, -everything- is an expression, so... of course you can return from there


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.


Nah, it's pretty cool once you get used to it


Kotlin has this, check out https://kotlinlang.org/docs/returns.html


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"

    unless python.major_version > 1 
other zen of python's rules that did not live up the expectations

- beautiful is better than ugly => tell that to numpy or pandas

- readability counts => (¬з¬)

- if the implementation is hard to explain, it's a bad idea => then the World is full of very bad ideas


Python is a communist apartment block, brutal in design and conformity and not very efficient.


I had to read the title five times to decide if I was supposed to read it. I'm sold.


I’m one of those weirdos who can inline a double negative instantly so, I didn’t read it yet but I probably will because I’m curious about the title anyway


Oh my dear lord I didn’t think I’d actually disagree with the f’ing article but I had like 8 cases today that made me wish I had a built in if-not keyword! I mean, every language with the billion dollar mistake probably needs a language feature designed to pay back some of that.


I think the author would probably be fine with an if-not. It doesn't hide the double negation like unless.


Are you saying you don’t want to have to apply De Morgan's Theorem any time you read a headline?


So you’d be in favor of dropping “unless” from the English language because it was misused in that title? :)


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.


There are two different kinds of double negatives and English actually has both.

E.g. `I can't get no satisfaction` is structurally equivalent to French `ne ... pas`, i.e the second `no` is a reaffirmation, not a negation.

On the other hand `read this unless you're not a developer` is a logical double negation and thus equivalent to `read this if you're a developer`. In practice of course there are usually implied subtleties that make the two not entirely equivalent (the same way synonyms are conceptually interchangeable but may carry different subtext).

EDIT: I can't actually think of any true "double negative" in English that isn't indirect (e.g. `indirect` being synonymous with `not direct` thus `not indirect` being synonymous with `not not direct` and `not not` cancelling itself out). The only direct forms I can think of behave like `not ... no` in my first example, i.e. re-affirmations of the negative rather than double negations.

I think most of the complaints about "double negatives" (where the Rolling Stones line is usually cited as an example) are stylistic preferences or intentional misunderstandings based on arbitrary prescriptions (which are usually based in other languages like Latin that are perceived as "purer" or "more sophisticated").


The phrase for what's going on in that Rolling Stones lyric is "Negative concord" and yes, it's normal in many languages and that includes a lot of non-prestige English variants.

"Ain't nobody got time for that" is clear, likewise "I didn't go nowhere" and "He ain't take nothing from nobody" and when something is not clear ("That ain't nothing" could mean it is, or it is not, something) context usually suffices.


> any true "double negative" in English that isn't indirect

A: Do you like your hometown?

B: Well, I don't not like my hometown

In boolean logic, not false is true, but in English there's often a middle ground. See also https://en.wikipedia.org/wiki/Law_of_excluded_middle


True! But that still falls into what I said about true double negations usually having subtext that justifies their existence. Compare "I don't hate it".

I'd argue that in "I don't not like my hometown" not-liking acts almost as a compound verb, similar to "disliking" (which is just using a Latin prefix to say "not"). Resolving the double negative results in information loss because it omits the subtext (which in this case is the important part of the answer).

In other words, saying "I don't not like my hometown" makes sense because "I don't like my hometown" implies negative emotions (disliking) rather than an absence of positive emotions (liking). The difference could also be conveyed in emphasis ("I don't like my hometown") but this is more subtle and easier to misunderstand.


Ain't nobody got time for that!


Funny how human languages converge.


yeah yeah


> In Italian a double negative is still negative.

Same in Spanish!

I think you're making a very good point here – understanding "unless" (quickly, i.e. without having to think about it) very much depends on one's understanding of the English language and/or one's mother tongue. Sure, one can probably get used to it (like one gets used to what "if/else" means etc.), but it definitely increases the mental effort needed to read & understand code.


I think we're going to have problems if we try to make all languages consistent for users of all languages.

If and unless are English language keywords besides being Ruby keywords; and in Spanish, the word for "if" and the word for "yes" only differ by an accent mark.


In Spanish I usually translated double negation like this:

"No! (Wrong!) There is no ..."

Bringing it back down to 1 level of negation, for it to make logic sense again.


I'd never use an 'unless !something', always an 'if something'


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."


I don't write ruby at all, but this is how I write my Java, Kotlin, and Javascript; fail-fast at the top of the method, with the happy path following.

And I'll take it one step further: I see a multi-line branch as a code smell in and of itself. If something in a branch takes multiple statements, I am of the opinion that the body of that branch should probably broken out into a method.

So if we're going off the article title, my code might look something like

  val isNotRubyDev = !user.isRubyDev
  if(isNotRubyDev) return
  
  // ...read article


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.

  def mute_mic  
    return unless mic_active?
  
    ..
  end
In this sense, you know that the entirety of the function is an expected (positive) case.


I agree guard classes are great with unless...other places not so much


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.


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.


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?

    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.

  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.


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.


>It reads like natural English

That's a problem for those who learn it as a second language and have to translate "unless" to "if not" every single time.


Don’t you know the word after awhile? Ruby core and standard libraries have many English words in them that are harder to learn than basic one-syllable words on the level of “if not.”


I'm mildly surprised the article doesn't mention that Perl, one of the languages that influenced Ruby, also has unless.


Common Lisp has unless too, I use it where it makes sense.


In Perl you can write postfix conditionals: `thing() unless $condition`, which at least is more natural usage of "unless" to a native English speaker than the other way around. That's the best way to use it IMO.


It's the same in Ruby. It's even mentioned in TFA.


Those who don't know history are doomed to repeat it. ;-)


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.


Fair enough argument for that particular example, but I think the author's point still stands for the other example (if !user.suspended do send_email end)


That example makes more sense as:

  send_email unless user.suspended?
Here we see that we normally send email - except for the exceptional case of the user being suspended.


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).


I think they mainly exist because `if' otherwise requires a `progn' if you want more than one expression in the true branch.


And using `cond' for 1 clause is a bit silly.

  (cond ((massivep building)
         (print-mass-warning building)
         (install-antigrav-module building)))


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/


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


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.


ternaries are expressions.

Just because of that they are far superior to if/else/unless, unless ;-) you're using the ternaries to execute statements with side effects instead of just returning a value.


Those are also expressions in ruby.


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


never_write_unless unless makes_sense_in_english && has_only_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.

   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.


Only false and nil are falsey, and the language shines for it. There is nothing weird about zero being a truthy value.


Yes, this is one of ruby's best features.


In what other languages does 0 evaluate to true?


Lua, Elixir, Common Lisp, Scheme, Racket, Clojure.

Most statically-typed languages don't let you evaluate 0 as a boolean.

IMO, 0 is a value, and values should be truthy. 0 being falsy is only a wart from C.


Thanks for that, I had no idea. Do you know if zero is truthy in erlang? It makes sense for elixir (based on ruby) but I'm wondering if erlang is that way too.


>Do you know if zero is truthy in erlang?

I don't know off the top of my head, but I would assume that yes, zero is truthy in Erlang as well, since Elixir is fully compatible with it.


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
    yours


It’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


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.


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).


How about 'ifnot'?

Although I like 'unless' in Ruby, Perl, CommonLisp (sometimes a condition is just naturally the other way of how 'if' wants it), I acknowledge that people get emotional and really hate it. Maybe a better alternative would be 'ifnot' to avoid the ! operator and the parentheses.


You need to use it when you want to be negative of ALL the thing after unless.

Basically, do_something unless A

Then it's simple now to manipulate the inside of A, as you only take care of the positive ! That's beautiful and simple design.

Example is you want to validate something.

raise Error unless someValidCondition.

Now decompose someValidCondition by just using positive connectives, simple right ?


You can contrive bad usage of just about anything in any language. Nobody actually writes "unless ... else". The few times it's come up it's been shot down in code reviews. unless is still perfectly fine for simple one line statements though.

Do I think Ruby NEEDS unless? No. Does this keep me awake at night? No lol


Use rubocop. Ban the use of `unless` except as a trailing conditional and ban double-negatives. Then the only thing you can do is `foo unless whatever?` and if someone comes along and modifies that to something horrible rubocop will yell at them that it is horrible (and `rubocop -a` will auto-fix it).

Use the tools.


Common Lisp has an unless macro, but it lacks an else clause, implicitly taking the value "nil" if the test expression is true. It's probably most useful for aborting the flow of the code, e.g.:

  (unless foo (error "Foo should not be nil"))
In less functional code, you sometimes see it for default values (though "OR" seems to be more idiomatic for that case):

  (unless foo (setf foo 3))
Regardless it has few of the downsides that TFA mentions (the sole exception being introducing a double-negative for unplanned compound test statements), but I don't find something like

  (unless (and foo (not bar)) (error ...)))
To be that unclear; we want both "foo" and "!bar" or error.


This can be served by:

  (or foo (error "Foo should not be nil")) 
plus it propagates the value of foo in the non-error case.


The visual difference between a ! and an l is not that great,

    send_email if !user.suspended?
or

    send_email if luser.suspended?
The latter could easily be an aggressive dev sending abusive emails to users they dislike enough to call lusers, at first glance at least ...


Perhaps a Python style spelt-out `not` for boolean negation would have been a better solution for this than `unless` in deep retrospect?

    send_email if not user_suspended?


Ruby has "not", but the precedence rules between "not" and "!" differ


Well I really showed myself up as commenting on a topic that I know nothing about! :-)

But that's actually ideal in this context, a really low precedence operator is exactly what you want because you can swap `unless` for `if not` without worring about extra parentheses:

send_email if not user_suspended? || user_opt_out?


I wouldn't use this. Relying on tricky operator precedence can trip up your readers. It's better to just wrap everything in parentheses. I also use parentheses when mixing && and ||, even though their precedence relationship is (probably) more widely known.

I generally avoid `not`, `and`, `or` keywords.


Funny, when I read your point, I was sure and ready counter with an argument that code editors must highlight symbols and unary operators differently (colours, maybe spacing as well). But I just checked this on a couple of Ruby and JS highlighters and they are actually not! :scream:


Sadly `¬` never caught on. But I suppose being visually similar to `-` isn't much better.


The "logical negation" sign used to have the vertical line as long as "|", so e.g. "¬A" looked more like "‾|A" but fully vertically aligned with the letter. Sadly, this glyph fell out of usage in fonts in favour of "minus with cedilla/descender".


I'd always understood it came from Frege's Begriffsschrift, a small descender from a horizontal line (part of a larger diagram), so like a short wide T, then lost its right arm later. Would you have some examples? (a quick google images shows nothing like that ...)


It was definitely a thing in Soviet works on mathematical/symbolic logic. But here's an example from Curry [0]. As I said, it was mostly a stylistic choice.

[0] https://www.google.ru/books/edition/Foundations_of_Mathemati...


If it's not a shift+<something> key on a popular keyboard layout, it's not going to gain widespread adoption in programming.


¬ is shift+` on the most common UK keyboard layout, but yeah, it would probably need to be popular in America.


I think most of the world either uses a US layout, or other layouts that don't have that key.


Brought back memories of alt.sysadmin.recovery.


As you learn more programming languages, these cute little operators become less cute and appealing, as you realize that !condition would be far more ubiquitous and transferable than something like unless condition.

I "grew up" in my career as a rails dev myself, but I favor things that are immediately clear to the broadest amount of people possible these days. And if you truly miss these constructs, in any language where you have metaprogramming, which is most these days amongst the broadly used ones, it should be trivially to implement them, should you insist on continuing to use them.

I use Elixir for my "fun" language these days, and they provide unless/2 as a macro, (if/2 is also a macro), but I haven't found myself reaching to use it once.


Assume all references to valid_token were to an opposite method named ‘invalid_token’ and suddenly the ‘unless’ will make much more sense.

X = 5 unless invalid_token

Much better than

X = 5 if !invalid_token

Here the humans inability to easily process double negatives, is now an argument for using ‘unless’ in the right circumstances.


The whole premise - that double negatives don't work for humans - is invalid. Natural languages have and continue to make use of double negatives and double positives (and in some cases, the positive-negative).

Obviously if your specific language doesn't make use of it, or makes use of it in a different way to ruby (e.g. is your natural language treating the second negative as a negation of the negative, or an intensifier of it?) then ruby's grammar will seem unnatural. But that's going to be true for anyone who's trying to map their natural language onto a programming language whose grammar is different to theirs.


To my mind, the point of `unless` is that is combined well with something that is unlikely but still possible.

  return the_obvious unless something_improbable_holds

  the_most_astonishing
This also align well with the lake of scalability of the number of condition: I doubt that in usual prosaic English its common to use "unless first-condition or second-condition or … ultimate-condition".

Of course it is logically equivalent to `if not`, but it is pragmatically not conveying the same information at human interpretation level.


Author of the post here: I've written a follow-up that addresses some of the rebuttals made in these comments: https://jesseduffield.com/Unless-Responses/

If I've neglected to address your specific criticism I'm happy to do so here


I take issue with the word unless. "Un" means opposite and "Less" means, well less, so unless means more. So `unless` should just be a straight alias for `if`.


The "un" in unless isn't the negating prefix "un-"; it's the word "on" with a change of vowel. The etymology is "on less[er condition than]," e.g., "Abort the launch on less than the rocket is deemed safe to fly."


You're missing that less is more. ;)


Sure. Except for every single existing use case of the word.


I used to write my if/else like the author advocates. The "true" condition first, followed by the "false".

A long time ago, a friend code reviewed and asked me to change to the most common condition first, as advocated in the Code Complete book (which I hadn't read at the time).

I still think it reads better if/else as true/false, but I am also big on having standards and a coding style, even if I disagree with them.

I mean, unless there's a lack of coding standard I will follow it.


I actually completely agree with the author. I remember coming across my first unless statement while trying to find the source of a nasty bug and I spent probably 5 minutes just trying to decipher the behavior of the unless block because my brain had some roadblock with the double negatives. This has become a lot easier to understand as I’ve used Ruby more but it still seems like a massive opportunity for cognitive complexity and errors for little gain


I'm not a very experienced Ruby developer but RuboCop does a pretty good job at guiding and teaching you to use `unless` as a guard in method definitions, e.g.:

  def my_method(required_thing)
    raise 'required_thing is required' unless required_thing.present?
    
    ...
  end
In practice, this hasn't been as issue as a consequence of the tooling and ecosystem around Ruby.


and why

    def my_method(required_thing)
      raise 'required_thing is required' if !required_thing.present?
is a problem ?


I don't think it's a problem necessarily. "unless" is more obvious to me since since it visually stands out more than "if !required_thing" while I'm casually scanning the code. So, in practice, it acts as an indication from the author of the code to the reader that "this line of code is a guard since we're using an unless".


It's a matter of taste.

I find inheriting code riddled with witty logic puzzles distasteful, so prefer my predecessor to have written Python, which constrained them from crafting too many.

Some people want a project to be a series of cryptic crosswords. For them perl/ruby/C.

When I write perl/ruby/C, I restrain myself from filling it with cool puzzles, but I usually don't get to inherit code from myself.


> The human brain, impressive as it is, grinds to a halt when parsing with double negatives.

Things like this sound like: "I have this characteristic, therefore everyone has this characteristic."

Some of us ain't got no problem with double negatives, and often find that "unless" tightens up code nicely, especially for single negatives.


I work with so many languages on a regular basis, I specifically avoid such language features, I won't even use the x if y syntax. My cognitive effort is better spent memorizing other specific things about the language.

That said, I think the author is overselling the need to not use such features. Despite my aversion to using such features, I have no problem reading them.


When I see unless in the prefix position (unless x then y) I have mentally read it twice. I reread it as `if not` because otherwise my brain can’t grok it. But if it is in the postfix position (y unless x) then it is super easy for me to grok. I think that is because how we would use it in natural language.


I find `if not` to be a far superior version, simply because it reads similarly to it's `if` counterpart, specifically for how conventionally condition methods are written in ruby.

`return if not valid?` or `return if invalid?`

Both make sense in my brain, whereas

`return unless valid?`

Feels like I need to make another connection to understand it


I've seen quadruple negatives in Python:

    if not no_unavailable_items:
      ...
    else:
      ...
unless on it's own is fine, but humans will misuse it if you're not careful.

You can address it with code reviews/standards/linters and get the best of both worlds.


I used to love unless, but now I prefer `if !`. Sometimes when I read `unless` i manually make the conversion in my head. The only problem i've run into is standardrb thinks otherwise and I've not mustered up the energy to bikeshed that change through.


I mean this is `ruby` for you, this is one of its principles, providing more than one way of doing something and making that thing "ergonomic and pleasurable". I don't think many devs who choose to use ruby would want to proscribe things like unless?


I've always felt that language features like 'unless' in Ruby and also things like 'on' and 'off' in Coffeescript fall squarely into the category of too clever. Simplicity will always win until the end of time.


To me your two statements here are in conflict with each other. I use "unless" in Ruby when it leads to simpler code. That ability to write code which reads simpler is its sole reason for existing.

(the headline however, is an exceedingly bad example)


Without syntactic sugar that differentiates a language from another, I see no way for a new language to become popular. So while some decisions may seem confusing, they helped gathering interest. IMO ruby and python became so popular because of their quirks.


Some people like, some people hate, that's why we have multiple languages to choose.


I found that if not opening new scope was way more annoying than option to use unless that nearly nobody picks. I.e

    if cond
        a = 1
    end
leaks a to outside and a will be nil if condition does not happen


Just to help people out. If you can't write out the title as a conditional statement in Ruby, you're not the intended audience. Though it still may be interesting as a discussion in conditionals in general.


I'm currently writing assembler for a virtual machine that has a "skiz" instruction.

It means "skip if zero".

The double negative always trips me up.

I had to write "don't skip if not zero" next to it the first dozen or so times.


Reminds me of my pet hate in user interfaces - having a check box for a negative condition such as "disabled" rather than having "enabled" and changing the default state of it.


I've been doing Rails for 10+ years and `unless x` absolutely breaks my mind. I have to internally convert it to `if !x` and evaluate it in my head 90% of the time.


For all of Perl's permissiveness, this is one situation that is explicitely dissallowed:

    unless condition
      # do A
    else
      # do B
    end


Elixir has the same "unless" statement but I hardly see it used in practice, and frankly I've never missed it, probably due to the reasons cited


Just me or anyone else find this heading impossible to parse? This particular combination of words seems to stick me in an infinite loop.


hmm, you're doing `unless` with `!` in condition you're doing it wrong.

    unless user.has_errors?
       user.save
    end
IMO better than `if !user.has_errors?`, at least for me: I will read it as "unless" in my head even in languages that only have `if`. You know what? I will just start using `unless` even harder, I will even make a rust macro for that.


common-lisp has functions called remove-if-not (applicative) and delete-if-not (destructive version).

The spec says that [t]he functions delete-if-not and remove-if-not are deprecated.

But all that is wrong is their double negative name. remove-if-not is precisely the same thing as keep-if, which is too useful to deprecate. Numerous languages have a function like this, sometimes called filter or similar.


Kinda similar, you oftentimes don't need to use an `else` statement. It is way overused and leads to code with too much indentation.


When reading code ! in condition can be overlooked relatively easily so I do not really see anything wrong with "unless".


`unless` is great. Rubocop (used everywhere) would flag and offer to autocorrect this title for the double-negation.


Unless would drive me insane. I do use IF NOT but when reading code my brain understands this format most clearly:

IF some-expression

ELSE Do-something


`unless` is generally used for guard clauses (eg `return unless authorized?`) or when there’s only one branch. You don’t tend to see `unless` used with multiple branches.


This, I’ve mostly used unless with guard clauses


So:

> Read this post ‘unless’ you’re not a Ruby developer

But:

> Read this post if you’re a Ruby developer

Surely?

Seriously though, the main way Ruby went wrong was an obsession with this entirely superficial kind of expressiveness and simplicity. Rubyists will lard their code with all sorts of "convenient" and "easy" features, to save a few keystrokes here and there, at the expense of bloating their APIs and hiding weird, hard-to-reason about magic.


The actual literal meaning of ‘unless’ has evaporated from my brain trying to parse that title


Reminds me of the

do not do <statements> unless not <expression>

of some fun programming language whose name I forgot.


Love Ruby. Hate unless.


You are now likely attracting all the Ruby devs here:D


Many languages try to be too cute. I was just reading up on the newish "Roc" language. There i saw List.dropIf and List.takeIf, thats basically just filter with a slipped bool check.


this title beautifully demonstrates the point. if there were an oscars for blogpost titles, this would be a nominee.


Do not use unless Unless it’s a one-liner


Clickbaity. Very clickbaity.


I don't like!.! Is not a word, but unless is. Unless is great unless you use it wrong.


and why does Rubocop’s Ruby Style Guide matter?


Ask the people who blindly apply and enforce Python pep8 which assumes everybody is on an 80 column glass terminal in the age of throwaway PCs having 16:9 screens.


TLDR: "I don't understand De Morgan's laws and so it's the languages's fault" :P




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: