I've done Ruby since 2014 or so at a few shops. My general anecdotal experience:
- Lone Ruby coders often use a lot of that cutesy/obscure/dense stuff.
- Teams of people writing Ruby, with healthy code review practices, tend to value simple, easy-to-read Ruby.
- A lot of Popular Ruby gems (and projects at aforementioned Ruby shops) have Rubocop-based style guides. Rubocop has some very strict ideas about Ruby coding style. The defaults are mostly quite sane and it's easy to disable/customize the ones you disagree with.
Not making excuses for some of the more florid parts of Ruby's syntax and stdlib, but in practice I do find things are manageable!
Things like that just often lead to bikeshedding. And then worse, powerful voices may approve ideas that work for them and not everyone else, or even more worse crippling a language to be too safe or bland to be truly useful.
I got into an argument a couple weeks ago whether an unprotected eval() in python should make it's way into production code of a fortune 500 company. The coder's argument was that, "Well the language has it so why not use it?"
"Because with great power, comes great responsibility..."
My understanding was the general bikeshedding around formatting was the reason gofmt won for golang. That seems to hint the problem is a programming wide culture. Just in the same historical context programmers have had around tabs vs spaces...
but that same thing will happen if the powerful voices are the ones taking the decisions for the whole language.
users.size
users.length
users.count
are all valid, all useful, all have different meanings, are all present in plain Ruby but with different meanings, and contain absolutely no information about what they do.For reference, .length loads everything and gets the length of the collection, .count runs a SQL COUNT query, and .size uses length if the query has already been run and .count if it hasn't.
Because for small datasets the difference doesn’t matter this results in beginner programmers choosing randomly, which 2/3 of time is the wrong choice.
users.select(count_star()).first(&connection)
which is really just a cleaned-up version of your suggestion.Of course, you could imagine an ActiveRecord-like API to make this nicer:
User.first{|t| t.count}
User.first{|t| t.count}.execute!
User.execute{|t| t.count}.values.first
# ...etc.
but the underlying problem is that building the query and executing the query are two discrete steps, Rails streamlines them instead of making the separation obvious, and when a system hides complexity it becomes harder to know what it's really doing.But Rails has definitely become a PR problem for the language in terms of how many complaints like this it leads to.
I consider myself a sensible developer, but even I’ve had some childish moments with Ruby: doing something I thought was clever and productive but which really just made my code inscrutable and unmaintainable...
...by other people. The flip side is I can just about figure out what I was trying to do when I read my own code: enough so that I still always use Ruby for personal projects.
Ruby is just too much fun and critically, to date, it’s the only language where I can write a page of code at the same speed I am thinking. It also usually executes first time without any errors, which is a joyous thing.
I am not claiming to be some kind of 10x uber hacker. Ruby just happens to be an incredibly wonderful language for thinking aloud with code.
In my experience, a company’s code should be like the style of English used in an instruction manual. It should clearly and plainly express the business logic behind the company’s mission.
Ruby is more like writing poetry. There’s nothing stopping one from writing boring, unambiguous, clear code in Ruby of course. As a Ruby developer, I’m sure your code looks very clear.
The reality is that other languages make it easier to enforce clarity. See gofmt for example, which standardizes Golang’s formatting, taking one more axis of “creativity” away from the less disciplined developers.
You can get it to discipline you as much or as little as you like.
Also, what kind of danger are we talking here? I consider mutation dangerous. Danger could also mean it has side effects or that it can raise an error.
I wouldn't call it inconsistent but I would call it arbitrary.
I would rather just name the method "#{base_method}_#{why_its_dangerous}". For example take ActiveRecord::Base#save and #save!
#save mutates and I would consider that dangerous. #save! can raise an error. I would rather name #save! as #save_or_raise_if_invalid. I know it doesn't look as pretty and takes longer to type but I really don't care. Then again, I'm not a big fan of exceptions so I would never call it.
All of those things are "dangerous" in this sense.
It is arbitrary, but that's how Ruby works; there's a gray area shared understanding of concepts. The "principle of least surprise" is literally about Matz, and Matz alone. Yes, something may be surprising to you, or not, but it's not about you. This shared taste/understandings/whatever is just how convention works. (Or at least, it did when I was involved with Ruby; it's been a couple of years so I'm out of touch!)
Anyway, I didn't realize who I was replying to when I replied to your first comment. I've seen a few of your presentations and read a lot of your work and I'm a fan.
Thank you!
I have no idea why the few languages that allow it do so. I've never wanted it and I can't imagine it makes parsing simpler in a language where you are sometimes compiling code at runtime and where parsing is already pretty wild.
I've said this before and been wrong so let's hope this turns out to have excellent use cases.
P.S. the 'endless method definition' is meaningless, too.
> def square(x) = x * x
The original reason of this proposal was:
> Ruby syntax is full of “end”s. I’m paranoid that the ends end Ruby. I hope Ruby is endless. --mame (Yusuke Endoh)
It sounds like "let's add a new syntax for lisp because it has too many brackets"
You'll find wisp [0] implemented for several of the larger Schemes. Often because the rationale for adding another syntax can be more nuanced and actually benefitial than a quick joke. [1]
Whilst you're right that caution absolutely applies when modifying a language's syntax, there may be a case for it that reduces friction for the programmer. The change might have been proposed with a joke. I doubt the entire rationale ended there, however.
[0] https://srfi.schemers.org/srfi-119/srfi-119.html
[1] https://srfi.schemers.org/srfi-110/srfi-110.html#rationale
Yes, there’s still some symbols, but nowhere near as many.
I fell totally in love with it almost 20 years ago but these days I think we have better languages to choose from even if their ecosystems aren't always as rich.
Which names do you believe are standard?
I get that they're trying to expose an SQL-like API, but yeah, map is always about mutation, but `select`only does mutation sometimes in SQL.
It’s not anything like C++ or C where the whole coding practice and culture changes depending on your framework (like using Unreal or doing Linux programming). The best practices were the same across the board.
Basically my experience with writing it for a decade doesn’t match these critiques. The quirks in Rails were easily ironed out among intermediate developers. It’s not like it requires advanced programming knowledge to just do what everyone else is doing, which is often share in best practice documents and popular libraries/tutorials/books or (less so) rubocop style plugins.
This is exactly the kind of stuff I hated when I had to work with ruby in my last gig and why I will never accept a job using it again - soo many pointless and inconsistent ways to do the same thing ... they have method aliases for collection operations like map/filter in standard library ! .NET went with non-standard SQL-like names (select/where) and I'm not a fan but at least they made their choice and stuck with it. And it's inconsistent all over the place - like '!' postfix means "operation mutates the object" in std lib BUT in rails it means the operation will raise an exception and not return an error code.
Now they add a pointless operator that means completely different thing in other languages to throw off even more people.
It's just a hell of a language to maintain someone else's code in.