Split off from #ideas > static dispatch
So far we've avoided opening the can of worms about spaces vs parens in function calls. And that's good, we don't have to and shouldn't make a decision now, it'd be a distraction.
But I do think we need to make sure that at least one acceptable solution exists. Having both ways is fine temporarily, but it could hurt if we're stuck there permanently. We should make sure we'll be able to find our way out of that situation before we add a language feature that requires parens.
This question will be resolved when it's clear that we won't end up stuck. This conversation will not continue to actually making a decision or designing the syntax.
Here are the options I currently see:
All of those have pros and cons, the question is whether we could see _any_ of them working out in the future.
A place to look for lessons and inspiration on this could be Elixir. Elixir allows spaces or parens in all situations. In practice it's mostly parens, except when calling macros (which are used for DSLs, and are mostly done with spaces) or defining 0-arity functions. But I regularly run into inconveniences with it, like code failing formatter checks, or looking at a different project that uses slightly different conventions.
One thing that could let us avoid this question: Would the static dispatch proposal work with spaces? Parens are an important part of making method calls feel familiar to people from other languages, but the same goes for parens for other function calls. Is it possible to separate them into two perpendicular proposals, one semantics proposal to add static dispatch, and one syntax proposal to add parens for function+method calls?
I think that as part of making roc more of an imperative looking language, it is important to try out parens. I do agree that for DSLs specifically spaces are nicer. But for most things, parens and dot changing work nicely. It is also is more familiar to a much wider audience.
I don't like the idea of keeping both but I understand that it may be needed for nice DSLs
I remember some other discussion of this in other topics, but I don't see a more recent topic dedicated to calling functions with spaces. What's the latest zeitgeist/consensus? Is this resolved? Any interest in a one-to-one alternative like |> .foo x |> bar y z |> .baz ?
The latest main has already upgraded to using Parens and Commas (PNC) syntax, we've also upgraded the platforms and some packages but haven't made any releases yet.
You can see what this looks like if you checkout the examples in basic-cli main, e.g.
https://github.com/roc-lang/basic-cli/blob/main/examples/countdown.roc
So the space based calling for functions has effectively been deprecated
Okie dokie. Do you remember in which thread the deprecation of spaces was decided, if not this one?
After re-reading the original proposal, I don't see why |> .foo wouldn't work just as well for static dispatch and autocomplete.
@Luke Boswell Do you know why so many ((...)) double parens in that countdown example?
We haven't implemented the sugar so call_fn!(()) is the same as call_fn!()
The plan is to have that before we make our next release... so hopefully way less (()) going on
Or actually... ({})
What about tick!((n - 1)) ?
The double parens (()) is probably a tuple being passed in
Seems like a naive migration from tick! (n - 1)
Oh, that looks like the formatter roc format --migrate just conservatively put/left them in, but we could remove.
Same with line 18
Luke Boswell said:
We haven't implemented the sugar so
call_fn!(())is the same ascall_fn!()
are you thinking of this as an intermediate step? We recently concluded we should go with first-class 0-arg functions after all
oh wait nm we can't do that until we have static dispatch
so yeah we do need the sugar as an intermediate step
I was thinking of ({}) above...
although in that plan, we leave {} as the unit type and reserve () to mean 0-arg function
Luke Boswell said:
Oh, that looks like the formatter
roc format --migratejust conservatively put/left them in, but we could remove.
Great, glad it's not intentional for expressions to need subparens
Specifically in the example I linked.
main! = \_args ->
Stdout.line!("\nLet's count down from 3 together - all you have to do is press <ENTER>.")?
_ = Stdin.line!({})
tick!(3)
tick! = \n ->
if n == 0 then
Stdout.line!("π SURPRISE! Happy Birthday! π")?
Ok({})
else
Stdout.line!((n |> Num.to_str |> \s -> "${s}..."))?
_ = Stdin.line!({})
tick!((n - 1))
main! = \_args ->
Stdout.line!("\nLet's count down from 3 together - all you have to do is press <ENTER>.")?
_ = Stdin.line!()
tick!(3)
tick! = \n ->
if n == 0 then
Stdout.line!("π SURPRISE! Happy Birthday! π")?
Ok()
else
Stdout.line!(n |> Num.to_str |> \s -> "${s}...")?
_ = Stdin.line!()
tick!(n - 1)
Luke Boswell said:
Oh, that looks like the formatter
roc format --migratejust conservatively put/left them in, but we could remove.
It'd be nice to add smart paren merging to the migrator.
Yeah... I'm guessing it's a trade-off, like the formatter preserves parens put there by the author, and the formatter probably has no real option to turn that off when doing --migrate, without making things more complicated (I assume)
JanCVanB said:
Actually, in this thread I don't see deprecation or strong blockers (cause record field access seems like a minor and manageable overlap). I don't see why |> .foo couldn't enable all SD features in spacesland.
My pessimism wants to make sure we didn't accidentally/silently deprecate something that had fans, and my optimism sees a path to dual support!
CC @Anthony Bullard
I expect there's another SD/breakingchanges thread that reached consensus to deprecate spaces calling...
It's definitely not easy to keep up between all the threads. That's why Richard's real world app has been so helpful, as it grips everything up into one place.
There's been some progress since then also... but it's currently the best example of "future" syntax we have
When you said spaces calling has "effectively been deprecated", did you just mean that any discussion on preserving/supporting it has been quiet lately?
Or do you mean that the incoming breaking changes include removal of support for it?
Well it's still there... but we're migrating all the platforms and packages over to PNC to experiment with that.
That sounds like there's no plan to stop supporting it in apps/userland.
I'm not sure if we plan on adding a Warning or going as far as removing it. I imagine we want to see more data from people experiencing PNC before we throw the bathwater out.
Hooray! I'm relieved to hear that. Thanks for clarifying :)
I infer that currently SD requires PNC, and if SD is a success then we can revisit Anthony's |> .foo syntax proposal to support SD with pipes/spaces too.
PNS = pipes and spaces?
Since so much progress has been made since that topic was resolved, would it require significant effort in the compiler to make x |> .foo bar work just like x.foo(bar) today?
Richard Feldman said:
oh wait nm we can't do that until we have static dispatch
Can't we support Dict.empty() as a zero arg function without methods?
yeah but it has to desugar to today's Dict.empty signature
the idea is:
.1 etc.) with statically dispatched methods (.get_1() etc.)() was only ever needed to close open tuples, we can now repurpose it to mean 0-arg function, e.g. () => StrThat's the right path to take
But
Parsing () is currently not valid
ha!
So we could implement just that now as a special thing
haha ok that seems reasonable then
it's not a breaking change if the thing it would break never worked :laughing:
There are a whole lotta "assert tuple isn't zero in length" in the compiler
Luke Boswell said:
Yeah... I'm guessing it's a trade-off, like the formatter preserves parens put there by the author, and the formatter probably has no real option to turn that off when doing
--migrate, without making things more complicated (I assume)
We can get more aggressive with this when WS application is removed from Exprs
My personal position is that as an abstract thing, I do prefer the aesthetic of Whitespace Application. But I really appreciate the benefits to both human and computer parsing that comes with having bounded terms. By that I mean that it is unambiguous without extra syntax when any term in the grammar starts and ends.
In the PNC world with fully bounded terms, if not for binops and operator precendence, you don't need parens to disambiguate.
I think it's more familiar, easier to teach, and easier to read for most people - not more nice to look at. The difference in readability goes up and the expression becomes more and more complex.
I haven't confirmed this opinion yet (still exploring the realworld app), but in abstract that tradeoff feels like making 90% of (simpler) expressions 2x less readable to make 10% of (more complex) expressions 2x more readable, which feels like a power user / library author bias.
When teaching Roc, we shouldn't be using many tuples, nested function calls, nested lambdas, etc. that benefit from bounding parens.
Roc is currently the most elegant scripting language I've ever seen, and aesthetics matter.
I propose that, aesthetically & in abstract,
par(ens) are to spa ces
as
camelCase is to snake_case
I'll risk undermining my argument by disclosing something though - I'm working on a project that values code readability by a non-technical audience, and I chose Roc months ago. I've recently started experimenting with DSLs for that prioritize readability & aesthetics over all else. However, I'd like to defend whitespace application on its own merits without accommodating the DSL use case or non-technical audiences (even though they do matter in various team environments).
Me making a biased last stand for whitespace:
love.jpg
Of course, if PNC-Only turns out to be Matt Damon, then we can probably retreat safely.
I don't have any arguments on aesthetic grounds - WSA vs. PNC is Picasso vs Rembrandt or something. I love the look for WSA on purely aesthetic grounds. But it seems that the vast majority of programmers don't. That could be a familiarity problem, sure. But it is what it is.
To me the groundbreaking, important part of Roc is the semantics. I wouldn't care if Roc looked exactly like a C-based language, or Python, or Elixir/Ruby, or was a LISP(though having a LISP like Roc would be hard to imagine). I care about the exact settings of the various semantics and what the resulting language looks like as a technical artifact not an art piece.
But there are a number of people that could really benefit from writing software using a language with those semantics that won't give a language with WSA a chance. And honestly, they still might not give Roc a chance without {} delimited blocks - I'm hopeful that Python existing reduces the chances of that. (And to be clear I am most fervently NOT suggesting we even think about introducing that). I don't want to see Roc stay a small, cozy little "functional scripting language" that suits my exact aesthetic choices. I want it to become, in time, an incredibly popular language across a number of domains that increases developer satisfaction and the quality of software written around the world.
JanCVanB said:
Since so much progress has been made since that topic was resolved, would it require significant effort in the compiler to make
x |> .foo barwork just likex.foo(bar)today?
If WSA isn't incompatible with Static Dispatch and its deprecation is based purely on the expected opinions of other people who've never written Roc before, it seems like a missed opportunity to never give WSA+SD a chance.
Can we trigger autocomplete when the space is hit (after 'x') in the case of x |> .foo bar? We'd have to trigger it on newline as well for a multiline |>
We could trigger autocomplete after |>, because in WSAland that's the equivalent intention as pressing "." in PNCland (and if we still have |> then we should be doing autocomplete after it anyways)
suggestions like
x |>
want .foo ? A.foo : B -> C
want bar ? bar : A, D -> E
want G.baz ? G.baz : A, F -> G
We could trigger autocomplete after
|>
A minor inconvenience compared to . but yeah, maybe we should try out WSA+SD now, because it seems much harder to resurrect it later
I suggested this to Richard
JanCVanB said:
Roc is currently the most elegant scripting language I've ever seen, and aesthetics matter.
hm, do you mean scripting specifically or DSLs? :thinking:
I actually think scripts (e.g. our website build script) are one of the cases where I actually have a significant preference for parens-and-commas aesthetically, largely because having a lot of ?s after closing parens is much less obtrusive than having lots of trys up front, which make things a lot more cluttered.
comparison on an example from our build script
I meant scripting in general, but I'm in a DSL-y headspace so maybe I'm not thinking general enough.
I haven't been following the latest ? plans - if we can have
Dir.copy_all! "public" "build" ? CopyAllFailed
then why not also
# try Dir.delete_all! "build"
Dir.delete_all! "build" ?
?
Which then seems identical to
Dir.delete_all!("build")?
We discussed that option... and it wasn't well received.
Because ? with a preceding space is a different operator
it wasn't well received originally
then later we decided to separately introduce the operator
so the reason we didn't go with it wasn't that it was taken, because at the time it wasn't :big_smile:
Okay, so it sounds like we could now make it mean the same thing in WSAland as it's about to in PNCland? With an error mapping suffix epilogue being optional?
Richard Feldman said:
...potentially enabling...
main = |_args|
# Check jq version
Cmd.exec! "jq --version" ?
# Create the build directory
if File.exists! "build" ? then
Dir.delete_all! "build" ?
Dir.create! "build" ?
# Copy public/ to build/
Dir.copy_all! "public" "build" ? CopyAllFailed
# Download the latest examples
Cmd.exec! "curl -fL -o examples-main.zip https://β¦" ?
Cmd.exec! "unzip -o examples-main.zip" ?
# Replace links in con/examples/index.md
replace_in_file! "con/examples/index.md" "](/" "](/examples/" ?
well there's definitely the problem that we talked about that design and didn't like it :sweat_smile:
Aw, well dang.
Is the consensus that it's just too many floating ?s? That only some of them should float?
it was awhile ago, I don't remember the exact points of the discussion
but it ended up with the conclusion that try was the best option
Are we now deprecating try and adding floating ?s anyways?
The ? operator is a suffix for unwrapping the Result as a function level and early return.
The ?? operator is for Result.with_default
I think try will go back to just a function... I'm not sure if we have a plan to deprecate that now we have ? working properly.
Maybe we should actually remove that altogether
try just falls out of Result.try being a method for free
From the example above, if the anchored+floating ?/??s will work with PNC
# PNC - Planned / Landing Now
Dir.create!("build")?
Dir.copy_all!("public", "build") ? CopyAllFailed
and we still support WSA for a bit
# WSA - Existing
Dir.create! "build"
Dir.copy_all! "public" "build"
then it seems the floating ?/??s can support WSA too
# WSA - Symmetrical Support
Dir.create! "build" ?
Dir.copy_all! "public" "build" ? CopyAllFailed
I'm not very confident that we'll support this long-term, but I'd be happy to review a PR to see you implement this
Or whoever
I might be overly fixating on mapping any proposed killer features of PNC to WSAland.
I'm still confused whether WSA is getting deprecated for vibes or blockers, though.
It seems like for vibes, which is fine if that's stated consensus.
Or perhaps for resource management of contributor hours! Valid!
WSA is getting deprecated
Richard has decided that we should experiment with PNC for various reasons. There are lot's of interrelated design elements which have been discussed. It's not just for vibes -- but an attempt to improve the dev experience.
When Richard originally showed the suggestion of parens in addition to whitespace, I said that we could maybe keep whitespace if we made it very clear when to use one or the other, but I said we should really stick to having one way to do things.
And he said not to worry about it, because everyone he showed the proposal to at that point agreed that they didn't feel like we needed whitespace anymore
So it feels like we've had a lot of overt positivity for PNC, and then fewer voices saying "no wait, what's happening?" at a delay
I'd be okay with either of them, I agree with you that whitespace looks better
But I strongly feel we need to end up with only one of them
One of the things golang gets really right is having a very consistent style
I agree by v1.
And it makes going into foreign codebases really easy
I agree that the decision doesn't need to happen now
I also love WSA and have been going through a process to adjust personally. I've been upgrading all the examples across the various platforms and packages... I've been putting my feelings aside and embracing the new world order.
After working with it, even just a little I think it's a positive direction... and I am pretty sold on the benefits particularly as we start to land additional features like SD and when the language server and tree-sitter catch up etc.
But I think we'd need to overcome the benefits of PNC for WSA to be the champ, ignoring the aesthetics
Actually, not ignoring the aesthetics, because we like the aesthetics of WSA more, but as Anthony points out, that's a minority position
Side note - I like that Roc has a BDFN and I can't think of a better BDFN than Richard.
Is |> considered deprecated too?
From my Weaver announcement article back in May:
The thing that gives me the most confidence that Iβll be using Roc at my job someday is the team of developers currently working on Roc. The GitHub repo is always active with new issues and pull requests, and I donβt have to worry about a low bus factor. Above all, the BDFN (Benevolent Dictator For Now) is Richard Feldman, who is a big player from the Elm ecosystem and the reason Roc exists at all. He is so knowledgeable about how to make the right programming language that I have no doubt that Roc will be a great language in the next few years!
It's why I'm here
It's a little awkward to chain things together using |> right now, and will be until we land SD.
|> is probably gone around when WSA is gone
Which is not going to happen anytime soon...
Oh! I'm used to deprecated things in Roc being gone within a couple of months. Is this expected to be a long deprecation?
Potentially with time for SD creep into WSAland?
I'm also a whitespace application aesthetics fan (obviously) but after spending so much time with PNC I looked at the example at the end of the homepage and was like "whoa that looks strange now!"
not bad, but I've already gotten used to the other default
and it feels comfortable now
I expect that my |>-centric style will adapt for what new patterns are most readable to me with any new syntax.
which might just require different boundaries between function defs to keep it reading English-y
It's funny because something that sounds like English is Engl-ish
Alright I'm gonna step away from yesterday's fork of roc-realworld where I started to WSA-ify every file to see how it looked.
Anthony Bullard said:
But there are a number of people that could really benefit from writing software using a language with those semantics that won't give a language with WSA a chance.
in a wild coincidence, I was just doing dishes and listening to Casey Muratori talk to ThePrimeagen (680K YouTube subscribers) about Prime wanting to pick a new language to learn and focus on in 2025 after being kind of bored with Go, and here's what he said on this topic: https://www.youtube.com/watch?v=xTgO6PpMnhk&t=1650s
Lol at the comments...
Prime being stuck in Tutorial Hell in 2025 was not on my bingo card.
Real answer: Create your own programming language.
I think people are going to really love roc when we get something close to 0.1.0 and add a little more polish. The PNC syntax is going to help people like Prime jump in and learn more about FP.
He seems rather... primed for Roc.
Yes, I was watching that live @Richard Feldman , and since then he has started learning JAI. (Which is very much not to my taste, no offense Mr. Blow)
When we have var, for, and static dispatch implemented - I think Prime would _love_ Roc
I should whisper him on Twitch and give him shit for telling me "people aren't really watching videos about obscure langauges" and then streaming him learning JAI for two days straight (and I expect it to continue)
In his community, JAI is definitely not obscure. I would argue that more people in his community are curious about JAI than are curious about ocaml (which is a much more popular and less obscure language overall)
This is true. He definitely has an imperative/procedural-focused audience.
And to be clear, this isn't me shitting in JAI (though I don't really see a reason to wait for it when it's kind of Zig that looks like Odin)
I'm fullying trying static dispatch now by migrating all of my scripts to it! Any suggestions for how best to SD-ify a function like this?
main! = |_|
"./input.txt"
|> Path.from_str
|> try Path.read_bytes!
# |> dbg
|> try Foo.from_bytes
# |> dbg
|> transform
# |> dbg
|> try Foo.to_bytes
# |> dbg
|> try Path.write_bytes! (Path.from_str "./output.txt")
Stdout.line! "π₯³ See ./output.txt"
SD isn't implemented yet... I'm not sure.
Ohhhh lol nvm
It's hard to keep up with the torrent of new features :smiley:
I'm excited for how many places it looks like
Foo.do(foo, bar) and get_foo() |> Foo.do(bar)
can shrink to
foo.do(bar) and get_foo().do(bar),
but I'm unclear whether we'll improve/preserve the readability of functions with lots of newlines & :pizza:s.
My guess is something like
main! = |_|
"./input.txt"
.(Path.from_str)()
.read_bytes!()?
# .(dbg)()
.(Foo.from_bytes)()?
# .(dbg)()
.transform()
# .(dbg)()
.to_bytes()?
# .(dbg)()
.(Path.write_bytes!)(Path.from_str("./output.txt"))
Stdout.line!("π₯³ See ./output.txt")
but that feels slightly less readable, so I'm hoping I just don't know the new tricks yet. (and I'll get more used to the new look over time)
(I edited the before & after to include a wider variety of functions.)
Right now's a very active time with other features releasing this week, so I can revisit this later!
Same with that other thread:
JanCVanB said:
Did we rule out
.$at some point
Oh, and I know that #ideas > static dispatch - dispatch on return types is working on making the from_ & write_ lines nicer, but I'm asking more about the newlines and parentheses.
JanCVanB has marked this topic as resolved.
Last updated: Jun 16 2026 at 16:19 UTC