Stream: contributing

Topic: Str.replace_each / replace_first / replace_last (#9596)


view this post on Zulip Dzmitry Misiuk (Jul 20 2026 at 22:11):

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.

view this post on Zulip Luke Boswell (Jul 20 2026 at 22:57):

Why not add find_last first?

view this post on Zulip Dzmitry Misiuk (Jul 20 2026 at 23:25):

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?

Either works — just want it right before it spreads across the backends.

view this post on Zulip Luke Boswell (Jul 20 2026 at 23:31):

I think split_last is the clearer name for this

view this post on Zulip Luke Boswell (Jul 20 2026 at 23:32):

I wonder if we should rename Str.find_first to Str.split_first

view this post on Zulip Luke Boswell (Jul 20 2026 at 23:33):

## FROM
find_first : Str, Str -> Try({ before : Str, after : Str }, [NotFound])
## TO
split_first : Str, Str -> Try({ before : Str, after : Str }, [NotFound])

view this post on Zulip Luke Boswell (Jul 20 2026 at 23:34):

I think find_* makes more sense with the predicate.

view this post on Zulip Richard Feldman (Jul 21 2026 at 00:05):

I agree! :+1:

view this post on Zulip Luke Boswell (Jul 21 2026 at 01:16):

@Dzmitry Misiuk -- once we land these MiniCI fixes that are stacking up... we can kickoff fresh CI runs on your PRs

view this post on Zulip Dzmitry Misiuk (Jul 21 2026 at 01:36):

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.

view this post on Zulip Dzmitry Misiuk (Jul 22 2026 at 18:30):

#10291 (the find_firstsplit_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.

view this post on Zulip Dzmitry Misiuk (Jul 23 2026 at 09:06):

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