
Ruby adds experimental support for rightward assignments - im_dario
https://blog.saeloun.com/2020/08/31/ruby-adds-experimental-rightward-assignment
======
jbackus
Ruby's parser is notoriously complex; if I remember correctly, only a few
members of the core team even know how to maintain it without introducing
regressions.

The craziest part of this is that Ruby does not provide a full featured Ruby
parser, so its entire static and dynamic analysis ecosystem depends on a
(actually very high quality) 3rd party parser, begrudgingly maintained by
someone who (AFAIK) doesn't even write Ruby anymore:
[https://github.com/whitequark/parser](https://github.com/whitequark/parser)

When I see new language features like this, I think of how Ruby's entire
tooling ecosystem depends on the dramatically underfunded (and therefore
primarily goodwill) efforts of high output maintainers like whitequark and a
few others. Ruby's highly dynamic and untyped nature means these tools are all
the non-runtime guarantees you can get, basically. Epitome of digital
infrastructure.

Consider asking your company to fund some of these people:

* [https://github.com/whitequark](https://github.com/whitequark) (maintains parser)

* [https://github.com/sponsors/bbatsov](https://github.com/sponsors/bbatsov) (maintains RuboCop)

* [https://github.com/sponsors/mbj](https://github.com/sponsors/mbj) (maintains unparser and mutant)

\---

As context, I know this stuff intimately because I used to contribute heavily
to most static and dynamic analysis tools in the Ruby ecosystem
([https://github.com/backus?tab=overview&from=2017-12-01&to=20...](https://github.com/backus?tab=overview&from=2017-12-01&to=2017-12-31))
and used to track new ruby changes really closely: [https://cognitohq.com/new-
features-in-ruby-2-4/](https://cognitohq.com/new-features-in-ruby-2-4/)

~~~
YorickPeterse
I helped out with parser as well for a while, though it was mostly
documentation, triaging issues, that sort of thing. I think I still have push
access to the Gem, as at the time the project had a bus factor of 1 (=
whitequark).

Anyway, I remember just how frustrated whitequark would get every time CRuby
decided to make some random syntax change. I have a lot of respect for the
current maintainer(s) for putting up with the ever growing complexity of the
Ruby syntax. I hope Ruby stops changing the syntax on a regular basis, but I
doubt this will happen any time soon.

~~~
jbackus
Yeah, I don't remember any of the Ruby tooling ecosystem maintainers having
nice things to say about the various syntax changes they've added over the
years. I don't blame them.

These changes are always justified with a hand wave and a reference to
"developer happiness" and ergonomics. I think that justification is seen as
insulting when all the static/dynamic analysis tools obviously _actually_
deliver a lot of value to Ruby devs, and yet the basic tooling to support this
ecosystem (robustly parsing, traversing, annotating, rewriting, and unparsing
an AST) is not provided by the language.

------
kevin_thibedeau
This hurts readability of code with lengthy expressions. Knowing the target of
the assignment is usually more important than the particulars of the
expression when you're skimming unfamiliar code. Having the variable pushed
out to random locations on a ragged right side isn't helpful.

~~~
kemiller
One of Ruby's philosophies is to provide multiple ways to express the same
fundamental operation so that it can conform to your thoughts instead of
forcing your thoughts to conform to it. You can still do it the old way where
it makes sense. Whether this philosophy is helpful or harmful is another
debate.

~~~
choward
Until youre working on a team and your thoughts aren't the same as everyone
else's thoughts.

~~~
hrktb
That’s rubocop’s raison d’etre. I like that you can be completely free
exploring with code in the style you like, and “button up” when it needs to
become shipping/shared code.

------
jaynetics
To counter some of the complaints regarding readability, a major use case for
this is simply hacking stuff together in the REPL. Sometimes you notice you
want to have your result as a variable when you've just typed a line, and
other solutions for this problem (Ctrl+A or ENTER plus foo = _) are not
universal.

Disclaimer: I'm not a core dev but saw a discussion amongst them about such
use cases.

~~~
mcphage
This is the exact reason I've wanted this, for a long time. I even asked Matz
about it a conference once, probably 10-ish years ago.

~~~
stouset
This is a completely unnecessary feature. `_` is always assigned to the value
of the previous expression.

    
    
        >> 4 + 2
        => 6
        >> _
        => 6
        >> "abc" + "def"
        => "abcdef"
        >> _
        => "abcdef
    

Adding this kind of syntax for something that's already trivial to do is
honestly kind of distressing for me as a Ruby developer who's been around
since early 1.8.

I still strongly believe that the `key: value` hash syntax was a massive
mistake. That's a lost battle, but this is getting ridiculous.

~~~
jordoh
`_` being assigned the result of the last statement evaluated is an IRB
feature [1], not a ruby feature - hence the note that it is not a universal
solution.

[1]:
[https://github.com/ruby/irb/blob/master/lib/irb/context.rb#L...](https://github.com/ruby/irb/blob/master/lib/irb/context.rb#L344)

~~~
stouset
So?

Why does this need to be in the language when both major REPLs already support
it? Why must this be yet another alternative way to accomplish the same thing
as the way that’s existed since the first days of the language?

There is no argument in favor of this addition. It doesn’t allow anything new,
it provides a pointless alternative to the current way of doing things, and
the _one_ situation where it’s useful—REPLs—there’s already a feature that
renders it completely unnecessary.

~~~
jordoh
For better or worse, providing multiple ways to do the same thing is part of
ruby's philosophy. [1]

[1]:
[https://www.artima.com/intv/ruby.html](https://www.artima.com/intv/ruby.html)

------
iandev
> While the above pattern has become standardized, it feels somewhat unnatural
> as we read most of the spoken languages from left to right.

Is this true (the 'unnatural' part)? Reading left to right, `age = 42` can be
read as "age is 42" which feels perfectly natural in English. `42 => age`
would be read as "assign 42 to age"? This feels more awkward to me. I'm not
sure I understand why anyone would want Rightward assignment.

~~~
qbonnard
A math tutor of mine was actually pretty angry at computer science for writing
things like `a=1; a=2; //so 1=2 ?` I guess he'd rather have the "assign 42 to
age"... Except he'd probably read `42 => age` as "42 implies age", which would
probably cause him more sleepless nights ;)

~~~
toxik
He’d probably like a := 42 which is in a lot of languages actually! Also
Erlang and other fp’s do consider = equivalence.

~~~
Izkata
I noticed a year or two ago when I'm pseudocoding I seem to default to "age
<\- 42" which I kinda like more...

------
rsanheim
I love Ruby. I've been working with it as my primary language for 15+ years.

Its sad to see how they keep adding new, arcane syntax for fairly obscure use
cases, seemingly without any real clear justification. I wish Matz would raise
the bar on these changes - Ruby is mature and already has a very powerful,
complicated syntax that gets tricky for corner cases. I appreciate "more than
one way to do it", but not everything needs to have ten ways to do it when the
first three ways work fine.

------
zevv
Well, this is a nice coincidence, Nim also added support for rightward
assignments today!

[https://play.nim-lang.org/#ix=2x7r](https://play.nim-lang.org/#ix=2x7r)

~~~
moonchild
Funny you should mention it, raku just got the same feature!

[https://tio.run/##K0gtyjH7/7@4NEkhMy8ts8Lq0Gpbu0O7NVQSdRRUgG...](https://tio.run/##K0gtyjH7/7@4NEkhMy8ts8Lq0Gpbu0O7NVQSdRRUgGLFCkXlmgrVILatgkqiQi0XV26lgmdeCZCTnmrNxWVipGBrB@MUJ1ZCmP//AwA)

~~~
kbenson
Doesn't Raku essentially already have this with feed operator? [1]

1: [https://raku.guide/#_feed_operator](https://raku.guide/#_feed_operator)

~~~
moonchild
Not quite. For example:

    
    
      > my $x
      (Any)
      > 1 ==> $x
      (1)
      > 2 ==> $x
      (2)
      > 3 ==> $x
      (3)
      > $x
      [1 2 3]
    

If ==> were simply assignment, we would expect x's value to be 3.

------
EsotericAlgo
Here is a feature ticket. The usage in a data transformation pipeline feels
right and appropriate as in comment #20 [https://bugs.ruby-
lang.org/issues/15921](https://bugs.ruby-lang.org/issues/15921)

~~~
choward
I don't find that reason any more compelling than any other reason I've read.
I still don't get why it's better than what already exists.

~~~
adnzzzzZ
The example given in comment #20 provides a practical reason for it, in that
in some cases it makes things more consistent visually. But mostly these
reasons are subjective and some people will be more or less sensitive to them.
A lot of programming language design and tool design in general is not
completely rational or about functionality only.

------
nooyurrsdey
This is just feature bloat. Why is this needed?

~~~
ht85
Because after a few thousand hours of ruby, when you can - most of the time -
tell a variable form a function, or if a function is receiving a list of
argument or a hash, you now need another challenge.

------
Animats
Can you mix left assignments, right assignments, and assignments in
expressions all in one statement? That will be great for obfuscating code.

~~~
beervirus
It'll probably be implementation-defined...

------
bfuclusion
The right way to do this is the way Prolog does it. It doesn't matter if you
say VAR = 12 or 12 = VAR or 1,2,10 = A, B, C It does whatever it takes to make
the left and the right sides equivalent to each other.

~~~
thrwn_frthr_awy
That is interesting. How does it work when a variable is on each side?

foo = 1

bar = 2

foo = bar

~~~
nemetroid
It will share with you its wisdom (the last line is printed by the
interpreter):

    
    
      ?- FOO = 1,
      BAR = 2,
      FOO = BAR.
      false.

~~~
thrwn_frthr_awy
That’s neat. Is there a separate syntax for var to var assignment or are
variables immutable in Prolog?

~~~
nemetroid
Variables are immutable, but note that it's the contradiction that leads to
the "false" response, there's no issue with the assignment itself:

    
    
      ?- FOO = 1,
      BAR = 1,
      FOO = BAR.
      FOO = BAR, BAR = 1.

------
jcheng
Rightward assignment is pretty strongly discouraged in the R community, due to
the effect it has of burying the lede. That said, I always felt the same about
Ruby’s inline if/unless (`puts "still working" unless done`) so maybe it will
have a better reception in this community.

~~~
toxik
Interesting, that it became discouraged. Probably won’t make it into Ruby
proper then. I don’t fault them for experiment.

~~~
jcheng
Actually, this is a pretty good reason, and it's straight from the source
(creator of S, the predecessor of R):
[https://stackoverflow.com/a/51548423/139922](https://stackoverflow.com/a/51548423/139922)

> When you type a long expression only to remember at the end that it would be
> a good idea to save the result, a right-hand arrow allows you to perform an
> assignment without retyping the line.

(I don't do this personally, though. I just run the expression and then my
next command is `x <\- .Last.value`)

~~~
thebelal
I use right assignment in this way all the time, you should try it Joe :)

------
pqdbr
I'm also not a fan of spending this syntax card on this feature. The ... =>
... syntax might be needed for something more useful in the future.

------
drusepth
More HN discussion on this topic from 6 days ago:
[https://news.ycombinator.com/item?id=24369892](https://news.ycombinator.com/item?id=24369892)

------
hhsnopek
[question]: Is the main benefit of "Rightward assignments" readability and
does this provide more benefits?

~~~
zamalek
Ruby currently supports "Rightward conditionals":

    
    
        user.delete() if deleteUser
    

It is probably thought that this feature would result in similar readability
improvements.

~~~
choward
I don't find that more readable at all. I prefer:

    
    
        if deleteUser
          user.delete()
        end
    

It's easier for me to read. It doesn't try to hide away logic. And if the line
gets longer if the conditional gets more complex or I want to add an else I
can without reformatting.

~~~
kevincox
It works well for some cases. You end up with

return if a.nil? return if attempts < max_attempts

In this case "return if" becomes like a keyword and you can ignore it and
focus on the rest of the expression. It also seems very readable for other
simple values like `return false if ...`. But I agree, as soon as the code to
the left of the `if` gets non-trivial it is harder to read.

------
e12e
Uh.. I'm not sure I like this, in a language that already has a similar
boolean operator (although I suppose that goes for all uses of such "arrow"
operators). But still:

    
    
      a = 2
      b = 1
      b >= a # false
      b >= a => a # assign false to a?
      b => a # assign 1 to a?

~~~
dragonwriter
It already has at least two different uses for the exact sigil "=>" (hash
construction, identifying capture variable in a rescue clauses), which is
probably more significant than that it has a kinda similar sigil used as a
comparison operator.

Like natural language (but unlike programming languages constructed with
thought toward ease of automated parsing), Ruby syntax is quite context
sensitive.

------
r00fus
Is this really just syntactic sugar? None of the examples in the article
seemed to show why code would be more readable (array assignment maybe, but
the leftward seems just as readable).

------
wyuenho
I'm sorry but this "feature" is yet another design a hacker living in a bubble
came up with. Most people think and program in English, or languages very
close to English. In English I can read let x be 4ys and some change. I don't
know how to read the arrow. 4ys and some change implies x? If this is a
feature designed purely for speakers of SOV or OSV order languages like
Japanese, that's fine, but the dominant language in technical discourse isn't
one of those.

~~~
dragonwriter
> I don't know how to read the arrow.

As a description (which is an easier translation of this idiom into English
than an imperative) “is assigned to”.

Imperatively, English tends to prefer verb-object with implicit subject, so we
ought to be using a Lisp family language (setq “foo” 42) maps better to
English imperative structure than most popular languages that do imperative
assignment.

~~~
wyuenho
Complete English sentences in present tense are infix, with the subject and
object clearly stated. Implicit subjects only follow after the first sentence
in a paragraph or a phrase. Case in point is the words you've read so far.
"are", "follow" and "is" are verbs. This is why most programming languages are
in infix notation. Lisp syntax stayed is a historical accident. [1] Arabic is
a prefix languages, and people don't program in Arabic.

"is assigned to" is passive voice. I think we've all learned in writing
classes we should avoid it as much as possible because stating the active
voice is clearer.

[1]:
[http://www.paulgraham.com/icad.html](http://www.paulgraham.com/icad.html)

~~~
dragonwriter
> Complete English sentences in present tense are infix. . Implicit subjects
> only follow after the first sentence in a paragraph or a phrase.

This is true of the indicative mood, but not the imperative.

Giving commands in the indicative mood is not typical.

~~~
wyuenho
Yes that's true. That's why most languages have infix operators and prefix
function calls.

------
drchopchop
So, a = b becomes equivalent to b => a.

However, you already construct hashes like hash = {:a => b}.

Perhaps we can also add reverse hash construction like hash = {b = a} for
consistency (and additional madness)

------
lacker
This is true to Ruby's Perl heritage. There is more than one way to do
everything, even the most basic of operations.

Quiz: what does the following Ruby code do?

    
    
      a = b => c = d

~~~
regularfry
Makes me reject the PR for lack of clarifying parentheses.

~~~
copperx
Do you really punish everybody else because you can't remember precedence
rules?

~~~
regularfry
Do you allow code into your codebase that isn't obvious on a first read?

------
Igelau
This would be nice for situations where you have a lot of method calls daisy
chained together. Like builders or Java Streams.

    
    
       DoThing()
       .withSomeOtherThing()
       .onSundays()
       .withCharset(Blah.BLAH)
       .betterAddThis(x -> { ret stuff() })
       .build()
       => turducken
    
    

The whole assembly of the variable 'turducken' is drifting rightward. By the
end we want to get to the point rather than backtrack.

~~~
kazinator
It's possible to have a language design like this:

    
    
       DoThing()
       .withSomeOtherThing()
       .onSundays()
       .withCharset(Blah.BLAH)
       .betterAddThis(x -> { ret stuff() })
       .build()
       .into(turducken)
    

Where a.into(b) is really (into a b), which is a macro sugar for (set b a) and
not a function.

------
dragonwriter
Ruby, it should be noted, already has experimental support for rightward
assignments by way of pattern matching, so this is _really_ TMTOWTDI.

~~~
gscho
(There's more than one way to do it)

------
matheusmoreira
I expected this new feature to use the -> token but they went with => instead.

Is there any reason why they overloaded the hash rocket? Even the simple
examples in the article look like somewhat malformed hashes to me!

    
    
      3 => box_height
      [170, 65] => height, weight
      traits => height, weight
    

How does the parser tell them apart?

~~~
dragonwriter
> Is there any reason why they overloaded the hash rocket?

The fact that it's used for a kinda similar purpose in rescue clauses is a
factor.

> How does the parser tell them apart?

Unbraced hashes can only occur in last-argument-of-function position, so
unless it's naked (not a parenthesized subexpression) in that position, it
can't be an unbraced hash.

------
lxe
Why? Why does ruby have an infinite amount of ways to do the same thing?

~~~
lillesvin
As someone else said in these comments, Ruby offers multiple ways to express
things, so you have more options to "write how you think" as opposed to being
pushed to "think how the language wants you to write".

I'm not saying it's good or bad, I'm just saying that appears to be one of the
reasons for it.

~~~
throwaway4007
It seems the opinionated, directive "there's only one way to do it" approach
is winning over the hacking around, "wrapping around your mind" approach. Lisp
and Perl have had their times under the sun, now it's all about Python and Go.
Perhaps these things are cyclical.

~~~
lillesvin
I'm using both Ruby and Go a lot (also Elixir, PHP, Bash and others).

I wouldn't even say that they solve different problems for me, it's more a
matter of _how_ I need the problem solved. If I need something that runs on
other OSes than my Linux box preferably with as few dependencies as possible,
Go. However, I can probably code it faster in Ruby, so if it only needs to run
on my machine or if I know Ruby's available where it needs to be run, then
I'll probably go with Ruby.

I think, instead of arguing which is better, the argument should be about
whether they're useful. And for both Go and Ruby that's an "absolutely!" from
me.

------
jasonrojas
When can we start writing ruby bottom up?

------
actuator
Ugh! I love that Ruby is expressive but this will just add confusion when a
developer moves to Ruby for the first time. It adds a mental burden to be
aware about all these rules.

------
adamzapasnik
I love Ruby and its 10th ways of writing code to achieve same thing...

Eh, there is a reason why Golang has such a huge popularity.

~~~
lillesvin
It's not like Ruby has an insignificant amount of users either...

------
ww520
I like it. The next step is piping result forward.

    
    
       1.2 + 2 | double | abs => result

------
nchelluri
i was kinda shocked to see all the negativity in this thread. i looked at this
post and thought 'beautiful' and kinda mind-blown at the simple elegance of
it. ruby is one of those languages that was cutting edge in 1995 and is
apparently cutting edge today. it writes and reads like nothing else. it can
be made very, very, or too terse, but it can be made to read simply and
concisely and clearly with an economy of characters spent. i also am not a
huge fan of the metaprogramming madness. but ruby is a beautiful language and
i definitely

    
    
      'think highly' => sentiment
    

this post.

------
transfire
I wish they pay more attention to real issues and potentials. e.g. The
Refinement design is half-baked -- Can't refine module methods, can't DRYly
reuse monkey patch code as refinements...

------
thysultan
Left is right, bottom is up, and the world is upside down.

------
throwaway4007
>R language also has a similar way of doing this 42 -> age.

Ah yes, the R language, a model of clarity, beauty and lightness in the world
of programming languages. Truly an example to follow.

(And it's not like anyone ever uses that "feature" in the R world either.)

~~~
thrwn_frthr_awy
I don't understand what point you are trying to make here. Do you not like the
feature simply because R has a similar feature? Why not look at examples given
here and by others and decide/comment on whether it adds clarity or
expressiveness or not?

~~~
throwaway4007
I am making two points:

-Arguing that adding a feature is ok because "R does it" is comically wrong when one is acquainted with all of R's warts and superfluous stuff

-Not even the R world uses _that_ feature either, so we have actual empirical evidence that it's not that helpful to developers.

~~~
thrwn_frthr_awy
I wouldn't say an additional notes section at the end of article showing that
is syntax is not unprecedented is an argument that the feature is ok because R
does it. The author is simply giving additional context to allow readers to
form their own opinion.

It's fine to not like the feature–I don't know enough about its usecase to
feel one way of another–but your comment felt unnecessarily negative without
adding much to the discussion.

Knowing that R users have access to these syntax and choose not to use it is
interesting to consider, but that was overshadowed by your first statement.

------
kazinator
Awk rightward assignment:

    
    
       $ awk 'BEGIN { "echo foo" | getline var; print var }'
       foo

------
ericb
I love ruby, but this is awful. Hope it goes away.

------
awwaiid
Ummm. Feel like I'm being trolled.

------
dgellow
But... why?

