Hacker News new | ask | show | jobs
by strken 2094 days ago
The thing that most annoyed me about Rails (not Ruby) was that

    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.

3 comments

So why would you not just default to always using .size?
You usually want to know when you're hitting the database. "Hit the database if the value isn't cached" is useful behavior, but should probably be telegraphed a bit more precisely than just the word "size".
“size” is fine. The person you are responding to is trying to say that complaining about multiple options is a bit strange as you can just use one option. Why is it an issue that other options exist? Who cares. There are synonyms for English words as well, is that an issue as well?
They are not synonyms, they mean slightly different things. And that is the problem.

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.

So if 'length' was the only option and I'd have to do ActiveRecord::Base.connection.execute('SELECT count(*) FROM users').values.first or something it would all be fine?
Other ORMs separate building the query from executing the query, which is less ergonomic but more explicit. Drawing on Rust, https://diesel.rs/ shows how this approach would work:

    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.
"size" is not fine. If I'm conditionally hitting the database, I want that to be as explicit as possible.
There are no guarantees that the count will remain correct after the first run of the query.
I learned Ruby by itself and only worked on Rails code later... so I've had a lot fewer core Ruby complaints then most.

But Rails has definitely become a PR problem for the language in terms of how many complaints like this it leads to.

Ugh yes I don't like this as well. Even if they're all useful, I'd do it with one method that accepts some optional arguments, this is unnecessarily confusing.