From: "Eric I." Date: 2008-06-13T02:54:13+09:00 Subject: Re: [Discuss] ruby, enum all? bug Zach Dennis wrote: > Does anyone else think this is weird behavior in ruby 1.8.x ? > [].all?{ false } # => true > > The implementation of enum.all? defaults the result to true, and then relies > on iterating over the elements to set it to false. It seems like it should Daniel Parker wrote: > I agree, even though the docs do explain exactly what it does, it seems like > it shouldn't return true. Maybe returning nil would be more appropriate when > there are no elements. Craig Demyanovich wrote: > That the code does what the docs indicate is beside the point (though > it's good that they're in agreement ;-) ). The behavior is definitely > not what I would've expected, which is to return false. My vote is to > just return false when there are no elements. I like to avoid nil > whenever possible (which is almost always) because it forces client > code to use a conditional to check for it. I guess I'm a contrarian here, because I think Ruby's current behavior is correct, and I think I can make a decent case for it. These kinds of "edge" conditions can often be counter-intuitive, although if you look at the larger picture they make perfect sense. Weren't you a little put off when you first learned that n**0 == 1 (if n != 0)? Or when you learned that 0! == 1? But if powers and factorials didn't behave that way, a lot of simple stuff would break (like the formulas for permutations and combinations). First, the content of the block is irrelevant as it should never be executed when the collection is empty. The block Zach uses though is very well chosen, in that it really strikes his point home. Second, all? (along with any?) are defined by the Enumerable module, which is designed to with collections of various sorts, not just arrays. All a collection has to do is implement the each method, and Enumerable handles everything else. So the behavior of all? (and any?) should make sense in this very general context -- including sets, trees, linked lists, etc. Third, if *all* members of a collection have a property, then *any subset* of that collection should have that property. So if I have a collection of accounts that are all overdue, any subset of those accounts should be overdue as well, right? And the empty collection is a valid subset. [account1, account2, account3].all? { |a| a.overdue? } => true [account1].all? { |a| a.overdue? } => true [].all? { |a| a.overdue? } => true If all? on an empty collection simply returns false, then we lose this valuable property. Fourth, there is a symmetry between all? and any?. In fact, it's the same symmetry we have between && and || using De Morgan's Law: P && Q == !(!P || !Q) P || Q == !{!P && !Q) With all? and any? we have these equivalents: collection.all? { |member| member.property? } == ! collection.any? { |member| ! member.property? } # note two inversions collection.any? { |member| member.property? } == ! collection.all? { |member| ! member.property? } Let's take accounts and being overdue as our specific example. If in a given collection all accounts are overdue, then it *can't* be the case that *any *of the accounts are *not* overdue: [account1, account2, account3].all? { |a| a.overdue? } == ! [account1, account2, account3].any? { |a| ! a.overdue? } # note the two inversions Those expressions must be equivalent. And so should this: [].all? { |a| a.overdue? } == ! [].any? { |a| ! a.overdue? } # note the two inversions Changing the behavior of all? would break this identity. And note that just as: [].all? { blah } will always return true: [].any? { blah } will always return false. So even though it seems counter-intuitive at first blush, in the larger context it makes perfect sense, and it would be a mistake to change it. Eric ==== LearnRuby.com offers Rails & Ruby HANDS-ON public & ON-SITE workshops. Ruby Fundamentals Workshop June 16-18 Ann Arbor, Mich. Ready for Rails Ruby Workshop June 23-24 Ann Arbor, Mich. Ruby on Rails Workshop June 25-27 Ann Arbor, Mich. Ruby Plus Rails Combo Workshop June 23-27 Ann Arbor, Mich. Please visit http://LearnRuby.com for all the details.