Suffix rule engine using last-byte dispatch for English noun inflection.
This module transforms words by their suffix when they are not found in the
uncountables set or irregulars map (Tiers 1 and 2 of the engine). It handles
both pluralization (e.g., "leaf" → "leaves") and singularization
(e.g., "leaves" → "leaf").
How it works
The engine uses a three-step technique that compiles to efficient BEAM bytecode:
Extract the last byte —
<<_::binary-size(skip), last>> = wordpulls the final byte in one instruction.Dispatch via
select_valjump table — The extracted byte dispatches through function clauses (dispatch_plural(word, len, ?s),dispatch_plural(word, len, ?h), etc.). The BEAM compiler turns this into aselect_valinstruction — an O(1) jump table on the integer value, not sequentialif/elsechecks.Confirm suffix via sized-skip match — Within each branch, a second binary match confirms the full suffix:
<<prefix::binary-size(skip3), "sis">> = word. This is a single comparison, not a scan.
The result: O(1) dispatch to the correct suffix group, then O(1) suffix confirmation. No scanning, no regex compilation, no string reversal.
Dispatch branches
Pluralization
| Last byte | Suffixes handled | Examples |
|---|---|---|
?s | -sis, -xis, -ous, -ois, -us, -is, -itis, -ss | analysis→analyses, cactus→cactuses, boss→bosses; classical: cactus→cacti, arthritis→arthritides |
?e | -ife, -mouse | knife→knives, dormouse→dormice, otherwise append -s |
?h | -ch, -sh, -tooth, -fish | church→churches, bucktooth→buckteeth, swordfish→swordfish |
?y | consonant+y, vowel+y | category→categories, day→days |
?o | all -o words | photo→photos (irregulars handle hero→heroes) |
?x | all -x words | box→boxes; classical: matrix→matrices, index→indices |
?z | all -z words | waltz→waltzes |
?f | -f words (split by @f_takes_ves) | leaf→leaves, roof→roofs, bluff→bluffs |
?a | default -s | sofa→sofas (Latin -um→-a handled by irregulars) |
?m | default -s | drum→drums; classical: -um→-a aquarium→aquaria |
?n | -person, -man | salesperson→salespeople, fireman→firemen |
?d | -child | grandchild→grandchildren, otherwise append -s |
?t | -foot | clubfoot→clubfeet, otherwise append -s |
| default | everything else | post→posts |
Singularization
| Last byte | Suffixes handled | Examples |
|---|---|---|
?s | -ives, -ies, -ses, -zes, -xes, -ves, -oes, -men, -es, -ss | knives→knife, categories→category, boxes→box |
?a | -ata→-a (Greek), -a→-um (Latin round-trip) | traumata→trauma, aquaria→aquarium |
?i | -i→-us (Latin round-trip) | cacti→cactus, foci→focus |
?e | -mice, -people, -ae→-a | dormice→dormouse, salespeople→salesperson, antennae→antenna |
?h | -teeth, -fish | buckteeth→bucktooth, swordfish→swordfish (unchanged) |
?n | -children, -men | grandchildren→grandchild, firemen→fireman |
?t | -feet | clubfeet→clubfoot |
| default | no change | (non--s endings are not English plurals) |
Performance
Benchmarked at 173K iterations/second on OTP 28 / Elixir 1.19 for a 35-word batch covering all suffix types — 48x faster than regex-based approaches (3.6K ips).
Usage
This module is not called directly. Plurality.Engine calls
apply_plural_rule/1 and apply_singular_rule/1 as Tier 3 of the
resolution pipeline. Both functions expect a downcased word that has
already been checked against uncountables and irregulars.
Summary
Functions
Applies suffix rules to produce the plural form of a downcased word.
Applies suffix rules to produce the singular form of a downcased word.
Functions
Applies suffix rules to produce the plural form of a downcased word.
This function is Tier 3 of the resolution engine. It should only be called
after the word has been checked against uncountables (Tier 1) and irregulars
(Tier 2) by Plurality.Engine.
The word is assumed to be already downcased. The result is always lowercase;
Plurality.Style.match_style/2 is applied by the engine afterward.
When classical? is true, Latin/Greek suffix rules are used:
-us → -i, -um → -a, -ix/-ex → -ices, -itis → -itides.
Examples
iex> Plurality.Rules.apply_plural_rule("leaf")
"leaves"
iex> Plurality.Rules.apply_plural_rule("church")
"churches"
iex> Plurality.Rules.apply_plural_rule("category")
"categories"
iex> Plurality.Rules.apply_plural_rule("post")
"posts"
iex> Plurality.Rules.apply_plural_rule("")
""
iex> Plurality.Rules.apply_plural_rule("focus", true)
"foci"
iex> Plurality.Rules.apply_plural_rule("aquarium", true)
"aquaria"
Applies suffix rules to produce the singular form of a downcased word.
This function is Tier 3 of the resolution engine. It should only be called
after the word has been checked against irregular plurals (Tier 2) and
uncountables (Tier 1) by Plurality.Engine.
The word is assumed to be already downcased. The result is always lowercase;
Plurality.Style.match_style/2 is applied by the engine afterward.
Examples
iex> Plurality.Rules.apply_singular_rule("churches")
"church"
iex> Plurality.Rules.apply_singular_rule("categories")
"category"
iex> Plurality.Rules.apply_singular_rule("posts")
"post"
iex> Plurality.Rules.apply_singular_rule("analyses")
"analysis"
iex> Plurality.Rules.apply_singular_rule("")
""