text: Then, on a different project the customer required us to work with an Oracle database, and I learned SQL. What a difference! I could express joins in a succinct, declarative manner, and get the desired result. It made me wonder why we did it the other way for so long.
  What a great example Mark. Database joins and where clauses are a perfect example of where for loops can be replaced by more reusable declarative syntax to greatly simplify the most common use cases.
  Databases are also a good example of suffering the same problem, the failure to abstract beyond the primitive operations. Databases have one primitive operation for viewing data, the "SELECT". It's the one hammer that must fit all problems.
  When working with collections in Smalltalk, we've abstracted beyond that into a handful of commonly used methods starting with #select:, but also #detect:, #detectMax:, #detectMin:, #detectSum:, #reject:, #collect:, #inject:into:, #includes:, #contains:, #do:, etc..
  Some languages encourage abstractions and allow you to build upon your previous work, others don't. Sometimes this is a limitation of the language, sometimes it's a limitation of the culture.
  C# is an interesting example where the language has become capable, but the culture is seriously lagging behind.