I'm trying to create a concat
function that can accept either a string or a function as its second argument. Here's what I have:
main! = |_args|
Stdout.write!? ("hey" |> concat(Fn Ansi.cr))
Ok({})
concat = |str, str_or_thunk|
when str_or_thunk is
Str s -> Str.concat(str, s)
Fn f -> Str.concat(str, f(""))
This works, but I have a few questions:
Why do I need to explicitly tag functions with Fn
? I noticed that Str
works as both a tag name and type, but function types like (Str -> Str)
can't be used as tag names. Is there a technical reason for this?
Is there a cleaner/more idiomatic way to achieve this in Roc? Coming from other languages, I'm used to function overloading or polymorphic functions. What's the Roc way to handle this pattern?
What's the general preference in the Roc community for cases where you want a function to accept different types? Should I:
concat_str
and concat_fn
)?My use case is building ANSI escape sequences where sometimes I want to concatenate a literal string and sometimes I want to concatenate the result of a function call (like Ansi.cr
which returns carriage return).
Thanks!
p.s. I used ai to help me formulate this, sorry. my brain melts a bit cause it's 10pm
Oh. It seems like I need to also use Str as well.
Stdout.write!? ("hey" |> concat(Str "you")
okay, maybe that is not that bad, it looks clear
2+3. I would also use tag unions here. For function overloading we plan on adding static dispatch, but for this case I would probably still use tag unions.
Last updated: Sep 09 2025 at 12:16 UTC