Make filename return Nothing for paths with no filename component (resolves #4)#6
Merged
Merged
Conversation
Resolves #4. filename's (Maybe String) return type was dishonest: since split never yields an empty array, Nothing was unreachable. Now filename returns Nothing when the last path component is empty—the empty path, a bare root like "/", or a directory path ending in a separator—and Just of the last component otherwise. Reverses the docstring accordingly and adds focused tests for each case.
There was a problem hiding this comment.
Build & Tests
Checked out claude/filename-nothing-empty at 65e8307 — master (90a7235) + one commit, clean topology. Two files changed: path.carp (the fix) and test/path.carp (5 new asserts).
carp -x test/path.carp→ 37 passed, 0 failed.carp-fmt --checkandangler: clean (rc 0). CI: both jobs green.
Findings
- Implementation is correct and idiomatic.
filenameis now(Maybe.filter (Array.last &(split p)) &(fn [f] (not (String.empty? f)))). I traced the types against core:Array.lastreturns a copied(Maybe String), and sincesplitnever yields an empty array it's alwaysJust;Maybe.filter's predicate is called as(~f &x), sofbinds a&String, andString.empty? : (Fn [&String] Bool)matches exactly. Sound, no ref/ownership issue. - Probed beyond the suite. Empirically:
splityields[""]for"",["" ""]for"/",["foo" "" ""]for"foo//"→filenameisNothingfor each.Justcases:"a/b/c"→c,"/foo"→foo,".hidden"→.hidden(hidden files handled),"."→.(matchesos.path.basename(".")),"a/b/"→Nothing. All sensible. basenameis independent (usesArray.prefix) and unaffected — confirmed; it's the only other split user, and there's no other caller offilenamein the lib.- Docstring is reversed to match and reads accurately. No
CHANGELOG.mdin this repo (confirmed), andREADME.mdhas nofilenamedocs — nothing else to update.
Direction note (your call)
Issue #4 offered (a) define Nothing cases or (b) drop the Maybe. This PR implements (a) — which you listed first and illustrated with the empty-string example, so it's well-grounded. Two things worth naming explicitly:
- Merging this is the choice of (a) over (b). The code correctly implements (a); if you'd rather drop the
Maybeentirely (b), this would need redirecting. - It's a user-facing behavior change:
filename ""wasJust ""(per the just-merged #3) and is nowNothing. That's exactly the intent of #4 and fine pre-0.1 — just flagging it's observable for any caller relying on the old always-Justcontract.
Verdict: merge
Correct, minimal, well-tested implementation of option (a) from issue #4, with the empty-string case you named handled exactly. Builds, 37/37 pass, fmt/lint clean, no bugs found. The only open item is the (a)-vs-(b) direction, which is yours — merging commits to (a).
hellerve
approved these changes
Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #4.
filenamereturns(Maybe String), but becausesplitnever yields an empty array itsArray.lastwas always aJust— soNothingwas unreachable and theMaybewas dishonest.The issue offered two fixes: (a) define cases where
filenameshould returnNothing, or (b) drop theMaybealtogether. This PR implements (a):filenamenow returnsNothingwhen the path has no filename component — i.e. when the lastsplitcomponent is the empty string. Otherwise it returnsJustof the last component."a/b.txt"Just "b.txt""foo"Just "foo""a/b/c"Just "c"""Nothing"/"Nothing"foo/"NothingWhy (a) over (b): you listed it first and gave the empty-string example, it keeps the
Maybemeaningful (a directory path or bare root genuinely has no filename), and it's the smaller API break — callers already had to handleMaybe, and the only ones that relied on the old "alwaysJust, empty string for empty path" behavior are now steered toward the honestNothing. If you'd rather go with (b) and drop theMaybeentirely, happy to redirect.basenameis implemented independently (viasplit/Array.prefix) and is unaffected; it was the only other internal user of the split machinery. The docstring is reversed to document the newNothingcases, and focused tests cover each row above. NoCHANGELOG.mdin this repo, so nothing to add there.Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.