I wrote up a proposal for something I think could have a huge positive impact on Roc - https://docs.google.com/document/d/1siZalWbCI8dkW5wWRg_0KOXqiPMofkbwy0KdfQzp1H4/edit?usp=sharing
any feedback welcome!
edit: there is now a thread for a v2 of the proposal based on feedback in this thread! #ideas > Purity Inference proposal v2
Is "Dead code elimination is maximally effective" intentionally removed from the second list of benefits?
I feel like this proposal doesn't change that because things like import side effects would still be impossible (only functions can be effectful)
oops, fixed!
This is a fascinating idea. One note -- as an outsider who occasionally checks in on the ecosystem, I feel like the language is accumulating a lot of sugar and symbols. If you're in the community, adding them gradually probably feels fine, but right now (assuming this proposal would be added), i'm thinking about seeing !, _, *, ?, ->, ,, and =>. it feels somewhat overwhelming for a language bills itself as friendly. perhaps some of this is related to the desire to not have overly-general solutions (e.g. backpassing) and perhaps my perspective is totally off base. but this combined with the number of module types starts to feel either overwhelming or that some more general solution would actually end up being simpler. i hope this isn't coming off as negative, as i think the proposal is very cool, but wanted to just call out this perspective.
that's a great point!
i do think the simplification / merging of Task with Result feels intuitive
and very interesting
unison uses ' in signatures to represent effectful code, it is literally syntactic sugar for {} -> ...
i'm not sure i love it, but interesting prior art none the less
one related data point is that Ruby code has all of those symbols (except for * in types, which we're talking about removing anyway) and I haven't heard people finding Ruby hard for beginners to learn or read
drew said:
unison uses
'in signatures to represent effectful code, it is literally syntactic sugar for{} -> ...
yeah, I looked at how they do it and ! feels like a more natural symbol to me :big_smile:
agreed. it does feel a bit odd that there's an asymmetry between the value and type levels (e.g. ? vs !)
Overall I suggest you do either or. Both approaches have merits:
As pointed out above, ! is probably not the best symbol for this due to its semantic meaning in sentences, and also since it is commonly used as a "not" operator in many languages (sadly, not something roc does, but one can dream). Maybe something like $ or @ could be considered instead? And yes, in rust ! is also kind of inappropriate at macro call sites as it can be mistaken for a "not" operator which uses the same symbol. It would also separate it more from the ? operator.
interesting! I hadn't considered the Task and Future angle, although I think overall this would make the language more approachable to Rust programmers because read? is exactly how you'd read from a file in Rust and then unwrap the Result in a way that propagates the error :big_smile:
also worth noting is that in this proposal, the equivalent of Rust's Future becomes "don't worry about it, your I/O is all async automatically"
which I think has an even gentler learning curve than Task <-> Future :grinning_face_with_smiling_eyes:
Alexander Pyattaev said:
As pointed out above, ! is probably not the best symbol for this due to its semantic meaning in sentences, and also since it is commonly used as a "not" operator in many languages (sadly, not something roc does, but one can dream). Maybe something like $ or @ could be considered instead? And yes, in rust ! is also kind of inappropriate at macro call sites as it can be mistaken for a "not" operator which uses the same symbol. It would also separate it more from the ? operator.
some notes:
!foo in Roc already means "not foo" and has for several years - I think maybe even since the first version of the parser, although it's been long enough that I don't remember anymore! :big_smile: ! as a prefix (for "not") and as a suffix (for macros) and it being totally fine in practice was part of the reason I figured it would be fine to have ! as both a prefix and a suffix at the expression level in Roc (which is what we have today), but worth noting that this proposal would go back to having only prefix ! (meaning "not" as usual) at the expression level! suffix would mean "effectfulness" only in type annotations, and in type annotations there has never been a ! prefix105 messages were moved from this topic to #ideas > function effectfulness syntax by Richard Feldman.
:point_up: was a very long tangent about one topic within this proposal, so I'm moving it to its own thread in order to keep this one shorter in case people want to talk about other topics here without having to scroll through that long discussion of one topic :big_smile:
What the proposal wants to achieve is very good!
I feel like it is not the case, but maybe: with the glory of type inference, if an unannotated and effectless fn has already returned a Result, adding some effects to it does not force changes at the call-site (provided thos are unannotated as well)? Cause this could be a strong card againts "but function coloring..."
IIuc, user defined effect-polymorphic functions aren't allowed. This woul mean that for MyLinkedList implementation, I would have to implement a walk and a walkTask. Except, I wouldn't call it that, since task will preferably be out of the dictionary for users. MyLinkedList.walkWithEffect? While at naming: Task.forever and Task.loop would become Result.forever, Result.loop? The names seem odd, but nothing ppl couldn't live with.
The thunk-ed record builders were quiet daunting at first compared to the current but I'm not even familiar with the current version. Mainly because of the many symbols. The current version looked simpler. But after looking at it again, I realised how Task.parallel would work, while I couldn't imagine that for the version that didn't use thunks. I would have also made the said mistake of adding !-s at the end of tasks in record-builded, Task.parallels.
I belive the snippet has an extra 'List' in the function argument under the text: "So fx would be a type variable for the effectfulness (or lack thereof) of the function. Here's how this could look in List.map"
Norbert Hajagos said:
IIuc, user defined effect-polymorphic functions aren't allowed. This woul mean that for MyLinkedList implementation, I would have to implement a walk and a walkTask.
That is not the case. Users defined are allowed with this proposal.
I would have never guessed that ! works on bool arguments in roc =) To be honest, the Python's approach of having to write "not x" instead of "!x" feels, generally, like a better way (yes, it is less concise, but far far less error-prone).
Last updated: Jun 16 2026 at 16:19 UTC