Dealing with “field of a field” is what kills pluralization in languages that do not support it natively.
Dealing with the multiplicity of a field is not a big deal when dealing in a imperative way with a single level of depth (print(user.team) becomes user.teams.each {|team| print(team)}). The real issues arise you need to access the “field of a field” in a language that does not natively support “plural-first” expressions (like XPath, see other comment).
Suppose you want to extract the name out of the (single) Team to which a user belongs. Simple: user.team.name ⇒ "RedTeam".
Now suppose a user can belong to multiple teams, like in the article. user.teams.name ⇒ NoMethodError: undefined method name’ for []:Array`.
OK, let’s change that to user.teams.map(&:name) ⇒ ["RedTeam", "SkyTeam"]. Works, but you can feel that it is not as well supported as the single case.
But wait, why does a team have a single name? Let’s pluralize the team name as well. user.teams.map(&:names) ⇒ [["RedTeam", "SecurityTeam"], ["SkyTeam"]]. Again, it works, but it is much hard to process. Should we now process the team tames individually (as a flattened array) or by team (carring around arrays of arrays)?
And once you introduce optionality all these things become even more complicated.
Language support is necessary when you want to deal in a nice way with the pluralization of fields. But which programming languages do count as “plural-first”? I know of XPath. Which other languages are there?
I think Haskell + one of the various lenslibraries could approach this, with the caveat that going from “one” on “many” in a field would still require changing existing “lens expressions” (which loosely resemble XPath) unless you wrapped all your fields in a size-1 container (like the Identity functor) from the beginning.
If you did that, you could change the Identity into Maybe or [] and still use traverse to “go through” that container and change it, because all of those containers are Traversable.
But which programming languages do count as “plural-first”?
Does jQuery count as a language? /s
APL and its descendants maybe. But they are far from the kind of languages into which we would normally encode business rules today. I cannot think of a mainstream language that could handle this “field of a field” problem you identified in a way that is as simple as the singular case.
My most recent experiences dealing with these sort of pluralization problems have not been so traumatic though. I think what really helps is to try to do the change incrementally. E.g., implement the pluralization at the lowest level (e.g. DB schema) and then switch the singular code to be implemented in terms of this new plurality (e.g. have a User#team method in the model layer that would do teams.first) and keep all the the rest of the code that relies on the singular assumption. You can then start moving away from that singular assumption piece by piece, or even start implementing the new functionality that required plurality, without it being a huge headache.
I don’t find this convincing. YAGNI still seems like a better principle to me, though I do agree that there’s a grey area where it makes sense to pluralize things that you think are likely to need it in the near future even if they don’t currently need it.
I mostly come at this from the perspective of database schema design, and I think if you truly take the “pluralize everything everywhere because who knows, there’s some nonzero chance you might need it at some point” approach advocated by this article, it’s pretty easy to get yourself into a place where what should be fast simple operations become slow and complicated and hard to reason about. The same applies to in-memory data model design but at least there, you can hide some of the pluralization behind abstractions a little more easily.
Say you’re using a SQL database as your persistence mechanism and you want to store user information. Does your user have an email address, a real name, and a phone number? Nice, make a users table with all those values. But… maybe some day you’ll want to support multiple email addresses. And well, users can change their names. And they can switch phones. So then your “users” table is reduced to a “user IDs” table with just a primary key column and all the other things that used to be additional columns get moved to child tables of their own. Your simple NOT NULL constraints for mandatory values get turned into handwritten triggers and stored procedures that you then have to write tests for, adding a user turns into a dependency graph of INSERT statements that have to satisfy foreign key constraints, user lookups go from single-row reads using a unique index to multi-table outer joins, and on and on.
And then, if it turns out you never actually need to support multiple email addresses, you will have wasted a bunch of effort pluralizing and created additional opportunities for bugs.
Or you could have an ids table and a data table with 0 or more rows per id. Normally you’d query the data table alone and handle multiple result rows. Yes, updates become more complex (because you probably delete and rewrite all rows for the user). But the truth is that a singular to multiple transition in the database like this is pretty easy. The complexity comes in the code layer.
It feels like a graph database kind of thing: specify that there’s Nodes (Entities) and Edges (Relations between Entities) and the whole plural thing falls out automatically.
In SQL terms you’d say there’s two kinds of table:
Node tables with primary key and some data columns, no foreign keys allowed.
Edge tables with only foreign keys to Node tables (okay and maybe other Edge tables?)
If your business rule requires a less-than-many-to-many relationship, add that as a constraint on the edge table, if it turns out later that Widgets actually can have more than one Name, remove that constraint.
SQL ‘returns’ are naturally plural (multiple rows), your template system would just have to suck it up.
On the downside that’s some extra joins but it’d simplify ORMs and stuff so maybe overall a win and something which the query optimizer could be optimized for?
As long as you have all the data on the edge tables you wouldn’t even need extra joins. For the most part you’d just query on the edge tables which would look like most current designs but foreign key instead of primary key constraints.
I really appreciate that most XSLT/XPath/XQuery expressions natively return and work withnode lists, not nodes.
For example, if the XPath to get all the music albums in a collection is /music//album, then the XPath to get all URLs of all cover arts of those albums (when available) is /music//album/cover-art/@url. These expressions also automatically handle the case of missing cover arts of cover arts without a url attribute.
You cannot write the code to get a single result without writing the code to handle the n-result case. The code to handle a single result is almost always a specialization of the n-result case, not the other way around. (In most languages the code to handle n results is a generalization of the single-result case.)
This immediately made me think of option types - collections of 0 or 1 items. What about a programming style where everything is a collection with explicit size ranges?
Dealing with “field of a field” is what kills pluralization in languages that do not support it natively.
Dealing with the multiplicity of a field is not a big deal when dealing in a imperative way with a single level of depth (
print(user.team)
becomesuser.teams.each {|team| print(team)}
). The real issues arise you need to access the “field of a field” in a language that does not natively support “plural-first” expressions (like XPath, see other comment).Suppose you want to extract the name out of the (single) Team to which a user belongs. Simple:
user.team.name
⇒"RedTeam"
.Now suppose a user can belong to multiple teams, like in the article.
user.teams.name
⇒NoMethodError: undefined method
name’ for []:Array`.OK, let’s change that to
user.teams.map(&:name)
⇒["RedTeam", "SkyTeam"]
. Works, but you can feel that it is not as well supported as the single case.But wait, why does a team have a single name? Let’s pluralize the team name as well.
user.teams.map(&:names)
⇒[["RedTeam", "SecurityTeam"], ["SkyTeam"]]
. Again, it works, but it is much hard to process. Should we now process the team tames individually (as a flattened array) or by team (carring around arrays of arrays)?And once you introduce optionality all these things become even more complicated.
Language support is necessary when you want to deal in a nice way with the pluralization of fields. But which programming languages do count as “plural-first”? I know of XPath. Which other languages are there?
I think Haskell + one of the various lens libraries could approach this, with the caveat that going from “one” on “many” in a field would still require changing existing “lens expressions” (which loosely resemble XPath) unless you wrapped all your fields in a size-1 container (like the Identity functor) from the beginning.
If you did that, you could change the
Identity
intoMaybe
or[]
and still usetraverse
to “go through” that container and change it, because all of those containers areTraversable
.Does jQuery count as a language? /s
APL and its descendants maybe. But they are far from the kind of languages into which we would normally encode business rules today. I cannot think of a mainstream language that could handle this “field of a field” problem you identified in a way that is as simple as the singular case.
My most recent experiences dealing with these sort of pluralization problems have not been so traumatic though. I think what really helps is to try to do the change incrementally. E.g., implement the pluralization at the lowest level (e.g. DB schema) and then switch the singular code to be implemented in terms of this new plurality (e.g. have a
User#team
method in the model layer that would doteams.first
) and keep all the the rest of the code that relies on the singular assumption. You can then start moving away from that singular assumption piece by piece, or even start implementing the new functionality that required plurality, without it being a huge headache.jq.
I don’t find this convincing. YAGNI still seems like a better principle to me, though I do agree that there’s a grey area where it makes sense to pluralize things that you think are likely to need it in the near future even if they don’t currently need it.
I mostly come at this from the perspective of database schema design, and I think if you truly take the “pluralize everything everywhere because who knows, there’s some nonzero chance you might need it at some point” approach advocated by this article, it’s pretty easy to get yourself into a place where what should be fast simple operations become slow and complicated and hard to reason about. The same applies to in-memory data model design but at least there, you can hide some of the pluralization behind abstractions a little more easily.
Say you’re using a SQL database as your persistence mechanism and you want to store user information. Does your user have an email address, a real name, and a phone number? Nice, make a users table with all those values. But… maybe some day you’ll want to support multiple email addresses. And well, users can change their names. And they can switch phones. So then your “users” table is reduced to a “user IDs” table with just a primary key column and all the other things that used to be additional columns get moved to child tables of their own. Your simple
NOT NULL
constraints for mandatory values get turned into handwritten triggers and stored procedures that you then have to write tests for, adding a user turns into a dependency graph ofINSERT
statements that have to satisfy foreign key constraints, user lookups go from single-row reads using a unique index to multi-table outer joins, and on and on.And then, if it turns out you never actually need to support multiple email addresses, you will have wasted a bunch of effort pluralizing and created additional opportunities for bugs.
Or you could have an ids table and a data table with 0 or more rows per id. Normally you’d query the data table alone and handle multiple result rows. Yes, updates become more complex (because you probably delete and rewrite all rows for the user). But the truth is that a singular to multiple transition in the database like this is pretty easy. The complexity comes in the code layer.
It feels like a graph database kind of thing: specify that there’s Nodes (Entities) and Edges (Relations between Entities) and the whole plural thing falls out automatically.
In SQL terms you’d say there’s two kinds of table:
On the downside that’s some extra joins but it’d simplify ORMs and stuff so maybe overall a win and something which the query optimizer could be optimized for?
As long as you have all the data on the edge tables you wouldn’t even need extra joins. For the most part you’d just query on the edge tables which would look like most current designs but foreign key instead of primary key constraints.
I really appreciate that most XSLT/XPath/XQuery expressions natively return and work withnode lists, not nodes.
For example, if the XPath to get all the music albums in a collection is
/music//album
, then the XPath to get all URLs of all cover arts of those albums (when available) is/music//album/cover-art/@url
. These expressions also automatically handle the case of missing cover arts of cover arts without aurl
attribute.You cannot write the code to get a single result without writing the code to handle the n-result case. The code to handle a single result is almost always a specialization of the n-result case, not the other way around. (In most languages the code to handle n results is a generalization of the single-result case.)
This immediately made me think of option types - collections of 0 or 1 items. What about a programming style where everything is a collection with explicit size ranges?