My understanding of simplicity is that it can be nailed down to a few universal principles:
1. Facilitating local reasoning: I do not want to read the whole program to understand what a single line of code may or may not do.
2. Compartmentalizing concepts: There should be a single, easily identifiable feature for each of: defining classes of data, separating concerns between different code units, enabling code reuse.
3. Keeping abstraction leaks to a minimum: Abstraction leaks force corner cases to be handled outside of the abstraction (that is, low-level programming). So abstractions should ideally not be leaky, and leaks should only be tolerated if 1. there is no nonleaky (or less leaky) alternative, 2. the benefits of the abstraction outweigh the cost of handling the leak.
Simplicity leads to conceptual clarity.
On the other hand, ease of use is more about the pragmatic desire to invest the least amount of effort possible in making a working program. At its most short-sighted extreme, the easiest thing to do is simply following the path of least resistance. A more refined version is following design patterns that experience tells work okay.
Simplicity and ease of use are most certainly positively correlated, but it is perfectly possible for a horribly contrived construct to be easy to use, if its complex internal details happen to match nicely the problem domain one is working on.
My heuristic take (admittedly not justified on anything beyond aesthetical preference) is that simplicity is the more fundamental concept, and ease of use is just a corollary. And my impression of Ruby is that it seeks to maximize ease of use, resorting to non-simple means if necessary.
Indeed, it would be simpler if there was only "if" and no "unless", or multiple names for what are the same method (Array#size and Array#length), but (often) they're there to make things easier to read and write.
What would you expect [nil].to_s to be? Seems like a reasonable representation to me. I certainly wouldn't want "['']", and I also wouldn't want nil.to_s to give me "nil". In practice having nil evaluate to an empty string is quite useful.
I think nil.to_s == "nil" is pretty reasonable. At least, it gives .to_s an elegant definition that does not need to case-analyze the nil-ness of the elements of an array.
In any case, this just reinforces my point that Ruby emphasizes ease of use, not simplicity.
1. Facilitating local reasoning: I do not want to read the whole program to understand what a single line of code may or may not do.
2. Compartmentalizing concepts: There should be a single, easily identifiable feature for each of: defining classes of data, separating concerns between different code units, enabling code reuse.
3. Keeping abstraction leaks to a minimum: Abstraction leaks force corner cases to be handled outside of the abstraction (that is, low-level programming). So abstractions should ideally not be leaky, and leaks should only be tolerated if 1. there is no nonleaky (or less leaky) alternative, 2. the benefits of the abstraction outweigh the cost of handling the leak.
Simplicity leads to conceptual clarity.
On the other hand, ease of use is more about the pragmatic desire to invest the least amount of effort possible in making a working program. At its most short-sighted extreme, the easiest thing to do is simply following the path of least resistance. A more refined version is following design patterns that experience tells work okay.
Simplicity and ease of use are most certainly positively correlated, but it is perfectly possible for a horribly contrived construct to be easy to use, if its complex internal details happen to match nicely the problem domain one is working on.
My heuristic take (admittedly not justified on anything beyond aesthetical preference) is that simplicity is the more fundamental concept, and ease of use is just a corollary. And my impression of Ruby is that it seeks to maximize ease of use, resorting to non-simple means if necessary.