Regarding the @ positional-argument syntax: I mean, my use of Ruby dropped considerably years ago, but from a Ruby-philosophy standpoint it looks fantastic to me. Having to constantly make up (usually single-character anyway) names for those parameters in map/filter chains was one of the ugliest parts of the language, especially when there was no actually-good name for things like temporary tuples, and the Smalltalk-derived (I think) syntax for block parameters is so visually heavyweight: { |…| … } { |…| … } { |…| … } where all the stuff in the || is close to useless, as opposed to just { … } { … } { … }. Method references passed with &, while separately useful, are not a good substitute; they're often less readable (to me) because the form of the expression is obscured, and they break down much faster, for instance as soon as you need any single other argument, rather than just when the block gets complex enough that the parameter names become important for readability.<p>I can appreciate that it moves farther away from “requiring things to be explicit means you get fewer accidental barriers to moving code around or increasing/decreasing complexity”, though, as some of my recent work has been in Java and I've had an eye for that aspect of it. But the next time I do anything in Ruby I'll be remembering that this is there.
Allowing @1 and @2 instead of |a, b| is a horrible change to Ruby. In the ruby tracker Matz himself states this is a compromise. So now Ruby is getting half-baked compromises that look out of place, all to appease some people making requests.<p>If the requested functionality is so pertinent, then a <i>proper</i> solution should be made, in line with Ruby's style. Not cryptic sigil soup with @ and .:, etc.<p>If a feature exists, it will get abused down the line. I do not want to read Ruby codebases that have @1, @2, @3, etc instead of named variables. We all know that temporary code is permanent. Just making this syntax possible is terrible.<p>I have to say I'm disappointed. I have enjoyed using Ruby for the last ~5 years but I disagree strongly with this syntax change.
Syntax is hard. In general I think that adding a simple numbered argument access for blocks makes sense, and something like swifts $ syntax would look pretty natural, ie:<p>```
books.map { $0.title }
```<p>In the Ruby lang discussion on this they go through a dozen different possible syntaxes and they all have issues -- for example the $ is problematic because Ruby currently uses these for global variables.<p>Even so, IMO using the `@` symbol for this will make the language harder to learn. It's not a very nice thing to have syntax that is almost ambiguous to the human.<p>```
books.map { @prefix + @1.author }
```<p>I teach Ruby to a lot of engineers (of all different experience levels), and one of the nice things is that it's so easy to pick up and be productive quickly. But there are bits where it takes some practice, and I'm not looking forward to telling people, "The at sign means it's an instance variable. Well, except if you're in a block and it's @1 or @2 or something, in which case it's a positional argument." Oof.
Ruby 1.9 hash literal syntax was introduced as an evolutionary step toward keyword arguments. If the former didn't exist, the latter would be a special syntax similar to but incompatible with the existing idiom to use a hash in terminal argument position as keyword arguments. That is, without the ability to treat foo(:a => 1, :b => 2) as equivalent to foo(a: 1, b: 2), you end up with two versions of every API -- "hash kwargs" and "real kwargs". And you'd similarly lose the ability for existing APIs to migrate to explicit keyword arguments from hash kwargs.<p>Would I prefer it if Ruby had started out with Crystal's named argument syntax? Absolutely. But Crystal had the luxury of starting from scratch without any need for backwards compatibility.
I agree and disagree.<p>The more features the language has, the harder it is to read.<p>And using @ for positional arguments when it’s already used for instance variables is a questionable choice. But on the other hand, the syntax for these blocks has always been clunky.<p>But Ruby is all about options. You can do things the way you want, when you want. That’s the happiness you achieve. But it does become more difficult to read for newcomers and devs not up on the latest updates, which is a major sacrifice.<p>Some of these new features are blessings though. The safe navigation operator saves lots of checks and I wish I had it in JavaScript and other languages (rather than checking for a property first).
I don't necessarily agree but it's a fair argument and well written. It's probably impossible to satisfy everyone; if Matz drops these features the people who want them (and I'm sure they exist) will yell that the language is stagnating compared to Python 3 and Node. If he adds them, we are adding bloat. There is constant pressure to add more stuff to "evolve" the language, hopefully the community steer through this pressure to the right direction.
As a rubyist of 10 years, a few thoughts. I wasn't aware of these upcoming features, but to some extent they make a lot of sense. Except... yeah, those symbol choices.<p>I agree that @1, @2 etc look totally wrong, and maybe that subjective view will change - but scope-wise, having @-variables that are nothing to do with the current instance would utterly ruin scannability. On the other side, I suspect Rubocop will very quickly react to outlaw it, so no big problem as far as my code goes.<p>I very rarely use &method(:foo). I find it ugly, and &:foo is more useful in 98% of circumstances. Maybe I would use it if it were shorter - but .:foo seems wrong. I feel like it would, again, be harder to scan.<p>That said, the more I let it sit in my brain, the less passionately I object.<p>> Still, I was disappointed when the safe navigation operator as introduced, as it basically acknowledged that it’s fine to return nil<p>I think this is indicative of the crux of the problem. Optimizing for programmer happiness implies you know what programmers need. Some programmers have no problem working with nils - we've long since accepted them, even embraced them.<p>The safe navigator pattern almost gave us a way of implementing a null object pattern with literally no work, while screaming out to the world "this method accepts nil, so watch out". I would much prefer code that accepts nil as a fact of life than code that denies its very existence.<p>I feel that "hacky code" will be well served by these changes, and I think that "production code" will be better served by tight Rubocop rules. So, everyone's happy!
I'll come to the defense of &. - for better or worse Ruby is mostly used for building database backed web applications where nils are simply unavoidable without some extreme over engineering or awkward idioms. `user&.address&.street` is at least safer than `try` - `user.try(:adress).try(:street)` - will not raise an error that `adress` is not a method. You could try and apply the null object pattern, but then `user.address.present?` is quite a rabbit hole - and frankly nil is the right return value for a user with no address on file.
It's interesting to see that languages try to get features that other languages have.<p>In Swift they have position argument in block from the start<p><pre><code> users.map{$0.name}
</code></pre>
now they want to add Ruby's &:attr feature[1]<p><pre><code> users.map(\.name)
</code></pre>
Maybe the best feature user want is the one other languages have. Or this is just Blub Paradox[2] in real life.<p>[1]: <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md" rel="nofollow">https://github.com/apple/swift-evolution/blob/master/proposa...</a>
[2]: <a href="http://www.paulgraham.com/avg.html" rel="nofollow">http://www.paulgraham.com/avg.html</a><p>edit: grammar
> In my opinion half-baked micro-optimizations add nothing, but complexity in the long run, and should be avoided. I’m really worried that Ruby has been gradually losing its way and has strayed from its creed.<p>It seems to me that "Optimizing for programmer happiness" is bound to end up with these half-baked micro-optimizations. The very first example of what the author considers the essence of Ruby is a perfect showcase of that, in my opinion.<p>Without a criteria that establishes whether or not you've now optimized <i>more</i> or <i>less</i> for programmer happiness, your users will just create blub fortresses that they stay in. Since the entire tagline is based on subjectivity and the users don't have any meaningful foundational principles to appreciate, all they have is their opinions on that feature they don't see why they need.
Completely agreed. I find it hard to read anything but the simplest of @ positional examples, and code has to be written to be read.<p>I have been writing Go a lot more these days and greatly appreciate the simplicity of language. Coming from a Perl background, and the TMTOWTDI (There's More Than One Way To Do It) philosophy, I've seen how that can make for clever but hard to read programs and I'd have preferred Ruby to err in the other direction.
I was happy with Ruby and Rails until when i have to write my own abstraction. (something like has_many, belongs_to in ActiveRecord).<p>Ruby class/metaclass things is really cumberstone to me. It's not about the magic, it's about the complicated data flow through all the class abstractions that push me off Ruby.<p>I prefer functional abstraction to class abstraction whenever i can.
In “optimizing for programming happiness”, there's much more left to be defined than ”happiness”. I think the biggest ambiguity here is ”programming”.<p>”Programming” can mean a lot of things, and I think Ruby still tailors too much to the ”be able to scrap things together somewhat elegantly” meaning.<p>But once the project takes off, you want performance and safety. Performance is constantly improved, but it seems safety is completely overlooked. TypeScript & co. proved you can make a type system that doesn't compromise for flexibility, so I think it should be at the core of the Ruby language improvements discussions.
ah, <i>that</i> topic... Well, for what its worth, I mostly agree with the author, except I never felt Ruby has made me "happy" more than any other programming language with at least some semi-decent features. Its problems make up for its features and, at the end of the day, it has enough annoying parts to make me angry at times.<p>The new features, of course, is pointless. It solves no problem, but brings with it the typical downsides of adding a new language feature. It reinvents the wheel, but makes it triangle-shaped, and tells you it's only meant for crash-tests anyway.
I don't understand his problem with hash literals. Hash rockets are just ugly and verbose and needed to go (through they're still needed for some object types to be declared as keys).<p>The positional arguments I'm on-the-fence about. I enjoy them in Elixir, but at the same time I wish it had a little more flexibility to name arguments. And I like the clarity that named arguments provide.<p>The `then` alias is also a major improvement; `yield_self` is a confusing and ugly combination of protected keywords.
The author starts with ` named block parameters`. But Clojure had this. I always Ruby has similar thing. In Clojure you can do `%1` or `%2` similar to `@1` `@2` in Ruby. So I guess the feature were inspired by Clojure. Even Elixir has it. The author is also a Clojure developer so I found it's weird that he hate that Ruby syntax.<p>However, at the same time, I totally agree that Ruby has so much more thing to work and improve, not to worry about these thing. As a Ruby dev, I don't want a new syntax to catch up with JavaScript where new syntax come up every weeks.<p>Ruby should focus more on type system, performance. Even some type hint like PHP would be very helpful. Lots of Ruby gem kind of introduce type. Example in Mongoid:<p><pre><code> class Person
include Mongoid::Document
field :first_name, type: String
field :middle_name, type: String
field :last_name, type: String
end
</code></pre>
I would love to have some type so we can use it directly.<p>Another weak point of Ruby is its module system :(. Some module system like Python would be great where we can load thing and alias them to avoid conflict.
I was sad to see => aka "hashrocket" syntax go, and it still slips in sometimes... but it is nice being able to use the same hash syntax in Ruby and JS.<p>What I'm intrigued by is the author's suggestion that there's more to the change than a relatively minor syntax change. Is there more to the story? I always assumed that both syntaxes are functionally identical.
Personally, even though `.then` resembles A+ promise-like syntax -- I find that it feels more idiomatic than `yeild_self`. I remember Matz talking about this at RubyKaigi, and I wondered how it would work in practice; however, I enjoy that syntax much more than its predecessor.<p>Addressing some of the author's comments, I would agree with:<p>- Endless ranges<p>- The "@" syntax names.each { puts @1 }<p>I would love to see more dialogue on how these changes _could_ positively affect the language. Also, if these are adopted widely, there will be the inevitable Rubocop rule telling you that it prefers this syntax over the "legacy." I love Rubocop, but sometimes I feel there's no end to the journey to writing 'idomatic' ruby and the continued language sprawl definitely does not help on this front.
Shouldn't this<p><pre><code> h = Hash.new { @1[@2] = "Go Fish: #{@1}" }
</code></pre>
Be<p><pre><code> h = Hash.new { @1[@2] = "Go Fish: #@1" }
</code></pre>
For maximum syntax golf?<p>Or are positional arguments not working the same way as instance variables wrt to interpolation?
I've been primarily a ruby programmer for the last 10 years. I remember when I started to learn Ruby the "there's more than one way to do it" philosophy really bothered me, and I thought it would cause lots of problems. But I just haven't found that to be the case. Even though a lot of these language additions don't add functionality they do make day to day programming more pleasant. Maybe I just kind of think the same way as Matz.<p>I remember I thought &. was going to lead to loads of PHP style abuse, but I haven't seen that. My initial reaction to the new implicit block parameters was also "yuk" for all the reasons mentioned in the article. But I think they will generally be used responsibly for very small blocks, where people would typically just name their vars x, y, z anyway.<p>"There's more than one way to do it" did make learning a little harder, but you spend much more time as an expert than as a beginner, so I don't think languages should be optimized for beginners.<p>As for refinements, I have never used them, never seen them used, and I don't even really remember what they are. I think they were to save us from the evils of monkey patching. But in general the community has learned to monkey patch fairly responsibly, and I've rarely had a problem with it, except with a few fairly old gems.<p>As for new features that I think would really bring ruby to the next level - better functional programming support, better concurrency support, and optional typing would be fantastic (I know, hello Elixir. But I hear typespecs in elixir are meh).<p>Functions are only "first class-ish" in ruby - the block/proc stuff was fairly revolutionary to people coming from Java and C, but it is clunky compared to most any other language with first class functions. Javascript should never do something better than ruby.<p>Good concurrency primitives are also key for language adoption in 2019 - even though simple process based concurrency is simple and safe for probably 80% to 90% of the apps out there - when people choose languages they want the one that will handle 10,000 requests per second using 2 GB of RAM.
I learned some cool new features coming in Ruby 2.7 and I am very much looking forward to them now.<p>The rest of this post is drivel, complaints, and ends with begging questions answered in his opening bullets. How does this optimize ruby for happiness? Flexibility and choice.<p>Backwards compatibility is maintained. You can write the same old ruby you wrote before if you want to avoid change. I'm very happy to have some new syntax tools in the toolbox.
I like JavaScript's (old?) solution to the indexed argument problem: the implicit `arguments` parameter. I haven't bothered looking into the rationale for why that wasn't carried over into arrow functions, but I miss it when debugging programs written using ES6.<p>I'd be interested to know if anything similar was considered during the discussions around adding indexed arguments to Ruby.
As for refinements, the use case I imagine is when you need to be defining a method that <i>dispatches on the type of</i> objects that are not “yours”, especially where that dispatch plausibly needs to be extended with more types by other code. (If it doesn't need to dispatch on type, you can just define a function in a module and be done; if the dispatch never needs to be extended, you can kinda do the same thing, but it's much uglier.) Serialization is the most common case where I've run into this, where objects of different types need to be serialized differently; indeed the <a href="https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec" rel="nofollow">https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/Refineme...</a> gives “ToJSON” as an example refinement.<p>A different way of handling this exists in CLOS: methods are defined on generic functions, which are are namespaced in packages just like class names are, but importantly, the class package and the generic package have no pervasive special relationship: a class doesn't “own” methods. So I can defgeneric extravagant-imperial-licentiousness:freeze and then define methods for it on all sorts of types, and it won't clash with someone else's jawbreakingly-simplistic-obdurate-numbness:freeze method. A consumer of either or both methods can choose to import one of the symbols and/or refer to either or both of them by their full names.<p>In Ruby, I've observed two “traditional” ways to do this: (1) add an ugly decoration to the method name which can never be elided (which is what I did in some internal projects, and then I added a prettier module function for external consumption that just trampolined to that method); or, seemingly more commonly and much worse, (2) ignore collisions, define whatever name you like on whichever classes you like, assume that you have dominion over your favorite short name and/or that the semantics are “obvious” and universal and that no one will ever load a library that does it some other way on top of yours and thereby break all the code depending on your version, and thereby, when that sometimes happens, land the hapless integrator in the dreaded “monkey patch hell”.<p>Refinements supposedly provide a better way to evade (2). I didn't get to play with them enough to be sure that they do, and in particular ISTR this might not actually let you have a fresh method name which can also be extended elsewhere without requiring all consumers of the method name to be aware of all the extension refinements, which if so seems like a disaster for serious use. I feel like there's a better mechanism for this hiding somewhere in Ruby design-space, but I don't know what it is.
This is a good reminder as to why Ruby is often called "the bad parts of Perl." I really appreciate other programming language ecosystems (almost any modern ones besides Ruby) that have abandoned things like this as bad practice, while Ruby continues to double down on it.
><i>Here’s a list of changes and additions that I certainly never needed (or wanted):</i><p>A, the classic "features before I came to use the language are nice and dandy, features added after I've learnt it are bad" complain.