Following on from the List work on #9596, I'd like to move over to Str and take the replace family: Str.replace_each, Str.replace_first, Str.replace_last. All pure Roc — replace_each via split_on + join_with, replace_first via find_first, replace_last via split_on reassembly. One PR, one commit each. Anyone already on these?
Aside (not blocking): there's find_first but no find_last, so replace_last rebuilds from split_on — happy to add a Str.find_last mirror as a follow-up if you'd want that symmetry.
Why not add find_last first?
Makes sense — I'll add it as a low-level op mirroring str_find_first_raw (backward scan, returns slices), then build the replace_* on top separately.
One naming check before I wire it through the backends: #9596 already tracks this as Str.split_last —
Str.split_last : Str, Str -> Try({ before : Str, after : Str }, [NotFound, ..])
— the same signature as the existing Str.find_first. And List already uses split_first / split_last for the { before, after } split, keeping find_* for the predicate form (List.find_first : List(a), (a -> Bool) -> …).
So which name do you want?
split_last — matches #9596 and the List conventionfind_last — matches the existing Str.find_firstEither works — just want it right before it spreads across the backends.
I think split_last is the clearer name for this
I wonder if we should rename Str.find_first to Str.split_first
## FROM
find_first : Str, Str -> Try({ before : Str, after : Str }, [NotFound])
## TO
split_first : Str, Str -> Try({ before : Str, after : Str }, [NotFound])
I think find_* makes more sense with the predicate.
I agree! :+1:
@Dzmitry Misiuk -- once we land these MiniCI fixes that are stacking up... we can kickoff fresh CI runs on your PRs
The split_first rename is up as #10291 (marking ready). split_last (the new op) and the replace_* family follow as separate PRs on top.
#10291 (the find_first → split_first rename) is merged — thanks @Luke Boswell.
Next up: split_last as #10319 — a new str_split_last low-level op mirroring str_split_first (backward scan → the { before, after } slices) across the dev / LLVM / wasm backends + interpreter, plus the pure-Roc Str.split_last and a repl snapshot.
replace_each / replace_first / replace_last follow on top.
split_last (#10319) is merged — thanks @Luke Boswell.
The replace_* family is now up as #10332: replace_each / replace_first / replace_last, all pure Roc over the split/join primitives.
That's the full Str split/replace set from #9596 done.
Last updated: Jul 23 2026 at 13:15 UTC