0042 phonetic encoders refuse a null word - CyrilB1531/lodestar GitHub Wiki
Status: accepted ยท Date: 2026-08-20
#342 is what this records.
Soundex.Encode,
Nysiis.Encode and
Metaphone.Encode each have a string overload
that forwarded straight to value.AsSpan(). string.AsSpan() on a null string returns an empty
span rather than throwing, so a null word silently encoded to "" โ the same answer an empty
string gets. PorterStemmer.Stem and every
Snowball stemmer in Lodestar.Text.Stemming, by contrast, call Guard.NotNull first and throw
ArgumentNullException.
Both families take one string and live in the same package. A caller who pipelines a stemmer into a
phonetic encoder โ or the reverse โ gets an exception from one half and a silent empty key from the
other, for the same missing input. The silent half is the worse of the two: every null in a
dataset collides into the same code, and nothing says so, where the stemmer would have stopped the
pipeline at the first null instead of encoding one further and burying it in an index.
The phonetic encoders refuse a null word, matching the stemmers. Each string overload above
now calls Guard.NotNull and throws ArgumentNullException, the same exception type and the same
message shape the stemmers already use. The ReadOnlySpan<char> overload each encoder also exposes
is unaffected โ a span cannot be null the same way a reference can, so there is nothing there to
refuse.
No opt-in flag. The two families disagreeing was the defect; a flag would keep the disagreement and
just move the question of which behavior applies from "which type is this" to "which argument did
the caller pass," which is not simpler for a caller pipelining both. Lodestar.Text is pre-1.0
(0.3.2 at the time of this decision), so this is a minor-version change under SemVer rather than
a major one โ recorded here for whoever next bumps src/Lodestar.Text/Version.props and cuts a
release (CONTRIBUTING.md's Releasing), not performed by this change itself.
An empty string is unaffected by any of this: it is not null, and every encoder already accepts it
and returns "", which is a real answer (no letters found) rather than a stand-in for "unknown."
- All three
stringoverloads above gain anArgumentNullExceptiontag and Exceptions-block entry; their reference pages and the phonetics index state the one rule instead of the split. - The next
Lodestar.Textrelease names this as a breaking change to a public method's exception contract, in a package still pre-1.0. - The next phonetic encoder added to this package inherits the rule from the stemmers' own precedent, rather than repeating this question.
-
Accept
nulleverywhere, relax the stemmers instead. Loses the diagnostic the stemmers already give for exactly this input, and does not fix the inconsistency โ it moves which half of the package is silent. The issue's own framing already argues the silent answer is the worse one to standardize on. -
Gate the refusal behind an explicit option. Keeps both behaviors alive at once, which is what
a caller pipelining a stemmer into an encoder cannot use โ the failure they are trying to catch
would only be caught on the half of the call graph whose flag happened to be set. Revisit only if a
caller surfaces a real use for the empty-string answer on a
nullinput.