

Drat – Ruby has a double splat - kaspth
https://dev.firmafon.dk/blog/drat-ruby-has-a-double-splat/

======
bshimmin
This is probably quite an informative and interesting article (though I found
some of the examples troublesome to understand and I doubt they'd make it into
any codebase I had anything to do with)... but the twee tone made it agonising
to read!

~~~
ebbv
This applies to a large swath of the Ruby community for me.

------
daenney
This seems to be pretty identical in behaviour to what you can do with the
args and kwargs arguments to functions in Python and how you can expand a list
or dict when passed in as an argument. The one thing I haven't seen before is
how you can name the yielded arguments.

------
__david__
> options = { a: 'b' } > { c: 'd', __options } # = > { :c => "d", :a => "b" }

> I don’t know when this is useful.

Ah! When I was learning Ruby after being a long time perl user, this was the
one huge missing feature that I longed for. Perl splats automatically (you
have to use refs if you don't want it to), and that leads to nice option
construction:

    
    
        @opts = ($some_flag1 ? (option1 => $some_flag1)      : (),
                 $some_flag2 ? (option2 => { a => 1 })       : (),
                 $some_flag3 ? (option3 => [ 1,2,3 ])        : (),
                 $some_flag4 ? (option4 => [$some_flag4, 1]) : ())
    

But it was annoying in Ruby. The best way I found previously was with a bunch
of chained .merges, one for each option:

    
    
        opts = {}.merge(some_flag1 ? {:option1 => $some_flag1}      : {})
                 .merge(some_flag2 ? {:option2 => { :a => 1 }}      : {})
                 .merge(some_flag3 ? {:option3 => [ 1,2,3 ]}        : {})
                 .merge(some_flag4 ? {:option4 => [$some_flag4, 1]} : {})
    

Ugly and verbose compared to the perl (which is usually not the case in perl
vs ruby). This new syntax allows for:

    
    
        opts = {**(some_flag1 ? {:option1 => $some_flag1}      : {}),
                **(some_flag2 ? {:option2 => { :a => 1 }}      : {}),
                **(some_flag3 ? {:option3 => [ 1,2,3 ]}        : {}),
                **(some_flag4 ? {:option4 => [$some_flag4, 1]} : {})}
    

Which looks only marginally better, but allows nice things like interleaving
optional and non-optional arguments:

    
    
        opts = {**(some_flag1 ? {:option1 => $some_flag1}      : {}),
                :option1_related => "xyz",
                **(some_flag2 ? {:option2 => { :a => 1 }}      : {}),
                **(some_flag3 ? {:option3 => [ 1,2,3 ]}        : {}),
                :option4_related1 => "xyz",
                :option4_related2 => "pdq",
                :option4_related3 => "abc",
                :option4_related4 => "123",
                **(some_flag4 ? {:option4 => [$some_flag4, 1]} : {})}
    

That's quite nice.

~~~
rudolf0
This is also now in Python 3, both for single and double splats.

------
chris_wot
I love programming, if only for its vocabulary. Where else can you use bangs,
splats, sloshes, whacks and thunks on a daily basis? Apart from the comic book
industry that is.

~~~
cmpb
My favorite is the 'empty promise' from ES6:

    
    
      new Promise((resolve, reject) => {
        ...
        resolve();
      });

~~~
lobster_johnson
If you're just resolving, use this [1]:

    
    
      Promise.resolve();
    

[1] [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve)

------
nahiluhmot
I'm really not a fan of the keyword argument feature in Ruby, especially after
using ES6 for a good number of projects. It's essentially a special-case of
variable destructuring that works for one type of key, Symbols. Also, since
its coupled into the arguments of a function, there is no way to use this sort
of feature for simple variable assignment.

~~~
grandalf
I agree. Also, consider how much more democratic and thoughtful the process
that ES6 underwent to add destructuring was, compared to the way Ruby
evolves... many of us got to use the feature via transpilation long before the
standard was baked, which led to a much more solid feature.

~~~
sanderjd
Yeah, it's a real testament to the ES community. I'm amazed how much has been
accomplished recently in such a transparent and collaborative way. Hopefully
that sort of thing is more the "new normal" than an exception to the rule.

~~~
tomphoolery
It seems rather fitting that the Web's language would be developed in a
transparent and collaborative manner.

------
xigency
Is this very much different from how arguments work in Scheme or Lisp? Seems
like this might just be a feature of interpreted languages.

~~~
agumonkey
It's been said that many dynamic languages have taken deep hints from lisp, so
no surprise it's pseud-metalevel call protocol (apply fun <list>) is close to
ruby and python ones. Although I've never used (apply ...) with keyword
arguments.

------
lkrubner
I am reminded of something in Brian Carper's old post "Keyword Arguments:
Ruby, Clojure, Common Lisp":

[http://briancarper.net/blog/579/keyword-arguments-ruby-
cloju...](http://briancarper.net/blog/579/keyword-arguments-ruby-clojure-
common-lisp)

This reminds me of everything I love, but also everything I hate, about Ruby.
He writes:

\------

With even more added sugar, you can leave off the parens in Ruby function
calls. So this is pretty common in Ruby:

foo :x => 123 # => {:x=>123}

How nice and punctuation-less. But then things get ugly. What about this?

foo {:x => 123}

That won't even compile.

~~~
cronin101
Hah, I had to try this out to believe it.

You can do `foo d: {:y => 2}` or `foo :d => {:y => 2}` but not a raw hash
literal without parens.

That's parse.y[1] for you...

[1]:
[https://github.com/ruby/ruby/blob/trunk/parse.y](https://github.com/ruby/ruby/blob/trunk/parse.y)

~~~
tomphoolery
It's because Ruby thinks {} is a block. All Ruby methods can implicitly take a
block (why? who knows...), so the single-line block syntax is parsed as such
before it can be considered a hash argument into the method.

------
braaap
Although splat and option hashes (double splat before double splat came along)
are something I use everyday, I almost invariably end up thinking they
increase (cyclomatic) complexity. Quite often, having a more strongly typed
class as a method parameter instead of a hash is actually what I want. Double
splat and the like sometimes make it easier to do the wrong thing. Worse
still, they seem to promote it.

------
lloyd-christmas
Triple splat, no erasies, touch blue make it true.

~~~
gp7
, final destination.

------
batiste
Am I wrong to think that this new Ruby feature is just an inferior, more
confusing version of what is found in Python since forever?

~~~
batiste
Hey you have the right to downvote but an explanation would be welcome.

------
duaneb
It seems sometimes like the only thing that has changed significantly in Ruby
since I started with 1.8.4, ten years ago, is that it now has a real bytecode
and better garbage collection.

So sad to see a language stagnate like this.

~~~
matthewmacleod
Well, that's not true. Ruby's changed massively since the 1.8 series. What are
you expecting to see that you're not?

~~~
duaneb
> Ruby's changed massively since the 1.8 series

It was mostly syntactical, and it didn't do much to improve readability.

I guess I was hoping to see functional improvements, where they address the
pain points of ruby: performance, tooling, dependency hell, unicode support,
lack of things like co-routines or generators, the ability to restrict access
to internals.

~~~
indspenceable
Re: Generators - I don't know why it's so hidden, but the Enumerator class
allows you to make generators

```

enum = Enumerator.new{|y| a = 0; loop{y << a; a += 1}}

enum.next => 0

enum.next => 1

etc

```

I believe this has existed since 1.8x

~~~
cremno
What makes you think they're hidden? Enumerator moved from stdlib to core in
1.9 and also calling #to_enum isn't needed anymore:

    
    
        enum = 0.upto(Float::INFINITY)
        enum.next # => 0
        enum.next # => 1

