
New Features in Ruby 2.4 - jbackus
https://blog.blockscore.com/new-features-in-ruby-2-4/
======
saghm
Some of these are pretty amazing, like the `digits` method and the new
OptionParser functionality (which seems to be a sort of standard library
equivalent to doctopt!).

That being said, I can't help but shake my head at the section about "multiple
assignment of conditionals:

"You can now assign multiple variables within a conditional...You probably
shouldn’t do that though."

Am I alone and thinking that it's a little bit hypocritical to specifically
add functionality to your language if you don't want people to use it? Or is
this just a joke that's gone over my head?

~~~
jbackus
Ah well "You probably shouldn’t do that though" is just my opinion as the
author. I didn't play a part in these changes. I just like Ruby enough to dig
into their changelog :)

~~~
psyklic
Interestingly, assignment inside conditionals is usually avoided because it is
easy to accidentally do an assignment (=) rather than a comparison (==). Note
that for multiple assignment this mistake can no longer happen:

    
    
      irb(main): a, b == 3
      SyntaxError: syntax error, unexpected ==, expecting '='
    

Should it still be avoided? :)

~~~
jbackus
First of all that is an interesting point. Given that it isn't as potentially
dangerous I'll agree that it is less bad than I initially thought.

I'll respond to your question by asking you a question. Which of the following
expressions evaluate to "truthy"?

    
    
        'truthy' if (a, b = [])              # =>
        'truthy' if (a, b = nil)             # =>
        'truthy' if (a, b = [nil])           # =>
        'truthy' if (a, b = [nil, nil])      # =>
        'truthy' if (a, b = [false])         # =>
        'truthy' if (a, b = [false, false])  # =>
        'truthy' if (a, b = [true, false])   # =>
        'truthy' if (a, b = [false, true])   # =>
        'truthy' if (a, b = *[])             # =>
        'truthy' if (a, b = *nil)            # =>
        'truthy' if (a, b = *[nil])          # =>
        'truthy' if (a, b = *[nil, nil])     # =>
        'truthy' if (a, b = *[false])        # =>
        'truthy' if (a, b = *[false, false]) # =>
        'truthy' if (a, b = *[true, false])  # =>
        'truthy' if (a, b = *[false, true])  # =>
    

The answer is in this gist:
[https://gist.github.com/backus/c9b70dee67470698fd7d4a66ddf03...](https://gist.github.com/backus/c9b70dee67470698fd7d4a66ddf0384b).
Don't peek!

~~~
psyklic
Good exercise! So it may still be too easy to misunderstand when exactly the
condition will be true.

------
windor
Ruby is the first language I really like. The core concept of the language is
small, just not easy to get, but the change on how to programming is
significant. Sadly, Ruby is not the language for the multi-core world, and
many advantages of using it is disappearing. Although Ruby is changing, 2.3,
2.4, maybe 3.0, but what's the difference?

~~~
RVuRnvbM2e
Have you used concurrent-ruby?

[https://github.com/ruby-concurrency/concurrent-ruby](https://github.com/ruby-
concurrency/concurrent-ruby)

~~~
windor
Yes, from the beginning, and there exists celluloid which try to implement
actor model. The fact that concurrent-ruby cannot be truly parallelized just
disappoints me. The same idea of Future,Promise applied to Ruby doesn't gain
the benefit. At last, it's just a method of writing 'better' code, not getting
better solution.

------
jewbacca
> _If you are calling #sum on an array of non-integers then you need to
> provide your own initial value_

Why? This doesn't seem necessary, but, more importantly, would be inconsistent
with #inject's behaviour (if enforced).

\----

[http://ruby-doc.org/core-2.3.1/Enumerable.html#method-i-inje...](http://ruby-
doc.org/core-2.3.1/Enumerable.html#method-i-inject):

 _If you do not explicitly specify an initial value for memo, then the first
element of collection is used as the initial value of memo_

~~~
tomstuart
I assume (but don't know) that this is an attempt to avoid returning `nil` for
empty collections: `[].inject(:+)` is `nil`, but `[].sum` is `0`.

Forcing the use of the additive identity every time, rather than starting with
the first element of the collection, is a good way of setting people up to get
the correct behaviour on empty collections, even if they don't test for that
case.

------
Olap84
Will this be the last release before 3? Is there a defined schedule for 3 yet?
Or just "When it's done"?

~~~
riffraff
the target ETA for ruby 3 is "this decade" or 2020, as per
[http://confreaks.tv/videos/rubyconf2015-keynote-and-q-a-
matz](http://confreaks.tv/videos/rubyconf2015-keynote-and-q-a-matz)

so probably no, this won't be the last release before ruby 3.

------
ukoki
digits method just saved code-golfers 20 characters.

`num.to_s.split(//).map(&:to_i)` => `num.digits`

~~~
level3
As a golfer, that code would have already been golfed down to:

num.to_s.chars.map &:hex

So digits only saves 14 characters (22 if reverse is really necessary). But
usually we would actually be doing something with the digits instead of just
getting them, so the method may save even less.

For example, for finding digit sums, the new digits and sum methods give us
the efficient:

num.digits.sum

But for numbers up to 5 digits (or digit sums up to 47) we can already do:

num.to_s.sum%48

which is just 1 character longer.

------
mark_l_watson
Great to see my favorite language evolving! I also use various lisps, Java,
Haskell, Typescript, JavaScript, Python, etc., but I am happiest when coding
in Ruby.

Unfortunately, most of what I do now is machine learning, and Python wrappers
for underlying C++ code have by far more support than Ruby. (Ruby does have
some great ML projects and libraries though).

~~~
MrBra
Hopefully this will make it better!

[http://www.somatic.io/blog/tensorflow-is-coming-to-
ruby](http://www.somatic.io/blog/tensorflow-is-coming-to-ruby)

It's a Ruby wrapper for the popular Google TensorFlow ML library. It will
probably be completed before this autumn because it was originally a GSOC
project (and couldn't be admitted because it was found to be missing some
Google requirements at the last moment). It's now crowdfunded but it seems
that Google is still offering some degree of support to interested developers
[1].

In the meanwhile also check this out
[https://gist.github.com/gbuesing/865b814d312f46775cda](https://gist.github.com/gbuesing/865b814d312f46775cda)
"Resources for Machine Learning in Ruby", some of which are again wrappers
around popular ML C libraries, thus removing the problem about sheer speed.

[1]
[https://github.com/tensorflow/tensorflow/issues/50](https://github.com/tensorflow/tensorflow/issues/50)

------
sergiotapia
Ruby 2.4 adds a new #match? method for regular expressions which is three
times faster than any Regexp method in Ruby 2.3:

Why does Ruby have 4 different regexp match functions?

Regexp#match?: 2630002.5 i/s Regexp#===: 872217.5 i/s - 3.02x slower
Regexp#=~: 859713.0 i/s - 3.06x slower Regexp#match: 539361.3 i/s - 4.88x
slower

~~~
Olap84
So there is choice of course.

match returns match data, and sets the $~ variable

=== returns true or false, setting the $~ variable

=~ returns integer (position) or nil, setting $~

The newest one match? returns a boolean, not setting $~

~~~
thelibrarian
Also, === is the method used in case statement matching.

------
pmarreck
Ruby continues to serve as a continuously fresh inspiration for Elixir. ;)

------
toolz
Why do the old match operators now implicitly set a global variable? I feel
like in a year I'm going to be debugging some really weird race conditions
because of that change and I can't see any value in it...if I want to use the
value of a match operator I'll _always_ save it to a local variable.

Can anyone shed any light on this, because my first attempts at grok'ing this
change have me really blown away.

~~~
Lukas_Skywalker
They have been setting the global variable before. This is not a new
behaviour. I think it was to match Perls behaviour iirc

~~~
toolz
Ahh, thanks for the clarification I had no idea this was old behavior but it
looks like the new method might allow them to deprecate what I would consider
an anti-feature later.

~~~
anamoulous
This is the kind of stuff that is great for shell scripting / one liners. I
doubt they are ever going deprecate it. Maybe the best thing is to rewind your
mind 30 minutes and pretend you never read about it : )

Example

    
    
        ruby -ne 'puts $1 if $_ =~ /^(([\d\.])+)/' < /etc/hosts

~~~
toolz
I knew things would make sense the more I learned about it. Thanks, TIL. Now
I'll pretend I never knew about this so I'm never tempted to abuse it.

------
proyb2
This code doesn't make sense.

    
    
        123.digits                  # => [3, 2, 1]

~~~
gollahon
Usually when I've seen digits indexed in a number it occurs from right to left
(certainly in binary contexts--bit 0 is the least significant bit and bit 31
is the the most significant bit in a 32 bit integer). So, 123.digits[0] means
the least significant digit, 3, whereas 123.digits[2] would give you 1, the
most significant digit.

------
dkarapetyan
These are all great but when are we getting optional typing? If the js
ecosystem wasn't a clown operation my language of choice would be typescript
instead of ruby.

~~~
nazgob
It's not on agenda and it will never happen. There are so many languages with
fine typing, why break a dynamic one by adding stuff to it?

~~~
sundarurfriend
> It's not on agenda and it will never happen.

I believe Matz has said that it is on the agenda for Ruby 3.0: that it will
have static typing and that it will be optional.

~~~
nazgob
TIL, I guess that making Ruby 3.0 3x faster (3x3 initiative) and adding
optional static typing at the same time might be quite hard.

------
integrii
Ruby syntax is just getting goofier and goofier.

~~~
kimshibal
is this a sign to move to elixir?

~~~
jankiel
Not really, elixir's syntax is inspired by ruby.

~~~
xutopia
And that's a good thing! :-D

