Stream: ideas

Topic: Should join_with be a List function instead of Str?


view this post on Zulip Felipe Vogel (Nov 28 2025 at 03:22):

It is surprising to me that join_with can't be called on a List with static dispatch:

my_list.join_with(" ") # missing method error!

I understand that this is because join_with is a function in Str.

But since its first arg is a List, and it's intuitive to call it on a List as above, should join_with be a List function? Or am I missing something?

Complete example (run from test/fx):

app [main!] { pf: platform "./platform/main.roc" }

import pf.Stdout

main! = || {
  newline_separated = "1\n2\n3"
  list = newline_separated.split_on("\n")
  space_separated = list.join_with(" ")
  Stdout.line!(space_separated)
}

view this post on Zulip Felipe Vogel (Nov 28 2025 at 03:30):

I also realize this function doesn't take just any List, but specifically List Str.

On the other hand, there is already at least one List function that operates on a specific List type: concat_utf8 : List U8, Str -> List U8

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:42):

:thinking: I can't think of a reason not to offer a generic one:

join_with : List(item), item -> List(item)

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:42):

that would Just Work in this case, as well as others!

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:43):

also, congratulations @Felipe Vogel - this is officially the first time someone has made a feature request based on real usage of static dispatch! :smiley:

view this post on Zulip Felipe Vogel (Nov 28 2025 at 03:47):

Yay! I'm having fun with it .

Related question: how can I "break out" of static dispatch and call a function in a different module? For example, if I rewrite the above example in a "pipeline" style, my instinct is to use the pipe operator, but I know that's not available anymore in the new compiler:

main! = || {
  "1\n2\n3"
    .split_on("\n")
    |> Str.join_with(" ")
    |> Stdout.line!
}

view this post on Zulip Felipe Vogel (Nov 28 2025 at 03:48):

Also, should I go ahead and open a GitHub Issue for a generic join_with?

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:56):

yes please!

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:56):

can you link to it here after you've created it?

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:57):

regarding "break out" - we have -> which works just like |> in e.g. Gleam

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:57):

so you can do ->Str.join_with(" ")

view this post on Zulip Richard Feldman (Nov 28 2025 at 03:57):

(at least, in theory! there may be bugs in the new compiler :laughing:)

view this post on Zulip Felipe Vogel (Nov 28 2025 at 04:01):

Oh right, now I remember. I gave that a try just now and got a "not yet implemented" error:

-- NOT IMPLEMENTED -------------------------------

This feature is not yet implemented: canonicalize local_dispatch expression

This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!

Found 1 error(s) and 0 warning(s) for test/fx/my_test.roc.

Roc crashed: runtime error

When running the following program (in test/fx):

app [main!] { pf: platform "./platform/main.roc" }

import pf.Stdout

main! = || {
  "1\n2\n3"
    .split_on("\n")
    ->Str.join_with(" ")
    ->Stdout.line!
}

view this post on Zulip Felipe Vogel (Nov 28 2025 at 04:08):

Here's the GH Issue for generic join_with: https://github.com/roc-lang/roc/issues/8486

view this post on Zulip Richard Feldman (Nov 28 2025 at 04:14):

ah, I'll add -> support then!

view this post on Zulip Kilian Vounckx (Nov 28 2025 at 05:51):

Richard Feldman said:

:thinking: I can't think of a reason not to offer a generic one:

join_with : List(item), item -> List(item)

This would have different behavior than Str.join_with no?

Like ["a", "b"]->Str.join_with(" ") == "a b", but ["a", "b"]->List.generic_join_with(" ") == ["a", " ", "b"]?

view this post on Zulip Brendan Hansknecht (Nov 28 2025 at 05:54):

Yeah, I think that is why it is a function on str

view this post on Zulip Brendan Hansknecht (Nov 28 2025 at 05:55):

I think that is also why in python they switch it to be a method on the joiner " ".join(myList)

view this post on Zulip Richard Feldman (Nov 28 2025 at 12:07):

I'm just woke up and realized this and came here to say the same thing :joy:

view this post on Zulip Richard Feldman (Nov 28 2025 at 13:18):

interestingly, we could offer:

Str.join_with : List(Str), Str -> Str

List.join_with : List(item), item -> item
    where [item.join_with : List(item), item -> item]

view this post on Zulip Richard Feldman (Nov 28 2025 at 13:27):

then either would work

view this post on Zulip Richard Feldman (Nov 28 2025 at 17:19):

I'm kinda into trying it out, see how it feels

view this post on Zulip Norbert Hajagos (Nov 28 2025 at 18:59):

Love the idea

view this post on Zulip Richard Feldman (Nov 29 2025 at 03:32):

https://github.com/roc-lang/roc/pull/8495 fixes the -> operator!

view this post on Zulip Felipe Vogel (Nov 29 2025 at 13:52):

@Richard Feldman Thank you! -> is now working for local functions, but I'm getting errors when using it with builtins. I created an Issue with examples: https://github.com/roc-lang/roc/issues/8498

view this post on Zulip Richard Feldman (Nov 29 2025 at 14:00):

thanks! I'll look into that

view this post on Zulip Felipe Vogel (Nov 29 2025 at 14:06):

That last pair of type annotations above is really neat. The bulk of my experience with static typing is in TypeScript, where expressing something like that would take a lot more than an extra line. Even in Haskell it would require more ceremony.

view this post on Zulip Richard Feldman (Dec 12 2025 at 21:00):

added List.join_with in https://github.com/roc-lang/roc/pull/8651


Last updated: Jun 16 2026 at 16:19 UTC