I’m going to agree with both side so far - I find the !empty? to be cognitively heavier than it needs to be, and the current any? only working as expected when the collection type is none falsey is unintuitive and likely to cause problems.
Would it make sense to special case the any? method that doesn’t take a block argument to only check if the collection is not empty, regardless of truthy or falsey types?
When I read the block argument version, I instinctively translate to “are there any elements in this collection that look like this?”, while for the none blocked version I translate “are there any elements in this collection?”. The truthy VS falsey aspect is none intuitive to me unless I know it’s a holdover from the blocked version of the method. I the values of the collection matter to me in a truthy VS falsey way, I think I’d first try naively any?(&.itself). Maybe that’s just me, though.
@straight-shoota Btw, where’s the double-negation in the first place? Empty is a state, it’s not a negation. The whole thing reads exactly like it should - as an answer to a question Whether the collection is empty?, No, it’s not empty.
If we only talk about the specific circumstances of this instance or the general axiom to avoid any?, we’re not getting to the root of the problem.
I think Enumerable#any? is wrong.
The mere presence of a linter rule that explicitly and unrestrictively suggests to not use it, is a clear sign that something is at odds.
There are two meanings associated with any?:
Are there any elements in the collection.
Are there any truthy elements in the collection.
That’s a subtle difference, but for most cases it doesn’t matter because both semantics align.
The intention of Enumerable#any? is 2. but people tend to expect 1. That’s exactly what the ameba rule is about, to address the probable misconception about any?.
I think the semantics should rightfully prioritize 1. That’s what a reader implicitly assumes and it’s a far more common use case than 2.
Now changing this would be hard. It’s a breaking change and it’s a silent change of behaviour.
Maybe a possible route could be to deprecate Enumerable#any? for collections that can contain falsey types. That’s currently not possible, but we could enhance the compiler to allow issuing deprecations from macros. Then we could drop that in the next major release to make a hard break, and following that we could introduce it again in a minor release. That’s of course a long journey, but maybe it’s worth it?
An alternative solution would be if we could find another method name to express 1. semantics, then we could introduce that independently of any?. But I don’t have much hope for that.
Yes, empty is a state. But I don’t care about empty, I care about knowing if there is any element. Empty is the negation of that state. With negating that again to get what I actually need, I’m using a double negation.
size > 0 expresses the intent correctly, but it’s not a good implementation for generic connections. Enumerable#size actually counts all the elements. At best that only leads to bad performance. But it can have worse effects such as invalidating the entire collection or entering an infinite loop (Remove iteration in Enumerable#size · Issue #10014 · crystal-lang/crystal · GitHub).
Not read the full thread, but obviously any? is not same as !empty?, the invert version of any? is none?, but empty? only check if the count of elements is zero.
Have any? and any_truthy? seems to me as the best option (for crystal 2.x), maybe because I’m not a native english speaker but for a long time I thought that any? was the inverse of empty?.
I’ll just say that I’m the type that’ll take the readability of any? even with the gotchas. Just like I’ll use if ($something) in PHP rather than if (!empty($something)), even though I know they’re not quite the same thing (and protect myself against the edge cases in other ways).
Changing the semantics of Enumerable#any? would be nice. But it’s gonna take a long time.
So I’m thinking about a solution that can be implemented in a reasonable time frame.
I’d like to introduce a new method for determining if the collection contains any elements. It would return the negation of #empty?.
Following Collections: `any?` vs `!empty` - #10 by HertzDevilEnumerable#present? could be an option.
I’m a bit vary about this because it’s related to String#presence which considers whitespace as empty. So these semantics would be comparable to falsey items in a collection.
Another option would be Enumerable#populated?. It’s a bit longer but maybe a bit less ambiguous?
By the way, this is how #presence and friends would look like in Crystal if we copy ActiveSupport’s definitions:
class Object
def blank? : Bool
responds_to?(:empty?) ? !!empty? : !self
end
def present? : Bool
!blank?
end
def presence : self?
self unless blank?
end
end
struct Nil
def blank?
true
end
end
class String
def blank?
# same as before
end
end
ActiveSupport considers collections with blank? elements to be present?, so only String has the special treatment regarding spaces, and [nil, false].presence would return itself. (I’m not sure about the purpose of the other overrides.)
In my Japanese mis-understanding of English, I feel that it is normal for any to take a block. In Japanese, any is translated as “どれか(なにか) ~ がある” and the “~” usually acts as a block. It is not impossible to mean “there is more than zero” by not passing the block “~”, but but such sentences are rare.
Are there any elements in the collection.
Are there any truthy elements in the collection.
I just learned that 1 is preferred in English. If I have to choose between one of the two, I would definitely prefer to keep 2. I like the idea of present or some. (However, this is my personal choice and I would like to follow the community’s opinion.)
I remember that this kind of issue was discussed on slack ruby-jp before, but I don’t remember how the discussion went because slack deletes old posts…
With ChatGPT i was only able to find a few words that would match:
present?
populated?
filled?
My personal suggestion is some?, like in items.some? or errors.some?. But i am not a native speaker, so i’m not 100% sure whether some? would be a good fit. My second choice would be present?.