I'm just wondering if anyone would like to help with the work to upgrade to Purity Inference? My understanding is that Agus has made the changes backwards compatible, so this isn't an urgent or blocking thing, but helpful to find any issues.
I've been having fun working on it, and thought others might like to get involved too. I'm happy to help anyone if they would like to learn more.
I've started the upgrade for basic-cli here https://github.com/roc-lang/basic-cli/pull/257 and done all the platform/*.roc
files. Next up would be upgrading the examples/*.roc
files and getting those all happy with roc check
.
Then we need to update the host roc_fx_*
type signatures, and update the call from the host into roc (which is now so much simpler with purity inference).
I've completely upgraded roc-ray to purity inference, so we have that as an example if we need. You can see the code and test it out if you want here https://github.com/lukewilliamboswell/roc-ray/pull/38
I'm your guy
Do you have anything in particular that you already want to do, or is it just the stuff you listed?
Yeah just the stuff I listed. :sunglasses:
Sounds good. I probably won't have that much time for this, so we'd be happy to have any volunteers for this!
Could you list the things necessary to update a platform?
!
and change ->
to =>
. How did the interface between the platform and the host change? You write, that roc_fx_*
has to be updated. In which way?
The main difference is that effects that can’t fail would return their values directly instead of being wrapped in Result
, so you need to adjust the host to reflect that. If the effectful function returns {}
, the host side would just be void
. The name of the roc_fx_*
function remains unchanged. The !
suffix that would appear in the hosted function is dropped.
The other difference is that when you are running Task
in the host today (from the entry point), you have to unwrap the closure inside and call that, but now you’d get effectful functions instead that you can run directly
You can see these changes in Luke’s PR: https://github.com/lukewilliamboswell/roc-ray/pull/38/files#diff-fb36a3f95902a5f802b4bedd10bbe149ef6acb768f7815bf10f15ecbeb486595
That’s it for the host side
As Luke said, the compiler will be backwards compatible with Task
for a while. It detects what fx mode to use based on whether the platform’s hosted
module exposes effectful functions or Task
.
As a note, the changes to the platform API are basically going back to before we had task as builtin. For example, I updated wasm4 from before task as builtin directly to purity inference. The host basically didn't change. The one caveat is that I stopped returning a record of lambdas to the host and instead returned two functions cause that is simply nicer to use from the host.
So is it possible now to export more than one function? That is nice.
One thing I'd add is that purity inference hasn't landed in roc main yet. So the first thing I did was update the nix flake to point at the branch.
inputs = {
roc.url = "github:roc-lang/roc?ref=purity-inference";
nixpkgs.url = "github:nixos/nixpkgs";
};
Then I basically started updating the type annotations and just followed the compiler errors all the way through until I was completed (for the platform side). :smiley:
So is it possible now to export more than one function? That is nice.
yeah, this is in nightly currently. Don't even need purity inference for it. I added it a few weeks back
Makes a lot of apis a lot simpler in the platform. Anything to get away from dealing with closures
@Agus Zubiaga one thing to double check that came up in #compiler development > Early return and correct refcounting. We need to make sure that we don't reorder effectful function calls. I assume they all get toposorted with the pure functions currently. Unlike task where the await lambas enforced order, purity inference would have nothing to enforce order.
Yeah, just tested something really basic in roc-wasm4. This will reorder, which could be really problematic for side effecting ops:
i =
W4.trace! "this should print first"
j
j =
W4.trace! "this should print second"
1
outputs:
this should print second
this should print first
I think this, while being problematic, should still be outside of the purity inference PR
We should make a separate PR that:
By "efficiently feasible", I mean that even though errors saying "you defined y after x so you can't use y in x" would be helpful, I'm not sure if they require creation of a dependency graph, and I expect standard liveness checking would be a good bit more efficient
I think we still need the same dependency graph to check for things like cyclic definitions
we just need to not use it to reorder as many things as we are today
Oh, great catch! I forgot we did this
So we would reorder unless the def calls an effectful function, in which case we would report an error?
Or do we want to always require that defs are defined in order unless recursive?
(deleted)
Current plan is that everything is in order except for top level definitions.
Just makes the model simple
Oh ok. Except top-level and recursive functions, right?
I think we decided to disallow inline recursive functions
I’ll read the other thread
just be like if you need a recursive function, make it top-level
comes up super rarely (if ever) anyway haha
Ok yeah, that’s really simple :grinning:
@Sam Mohr are you planning to work on this change or should I?
I'm happy to work on it, but not planning on it
If you've got time, feel free!
If you don't pick it up, I'll do it once I finish return
As you point out, it's easy for either of us to do :smile:
Cool! Let’s just make sure we tell the other if we start it :big_smile:
haha sure thing
just be like if you need a recursive function, make it top-level
I assume a self recursive function should work fine even if nested? It is just mutually recursive functions that will be blocked?
right, yeah
I've been working with Luke to try to update roc-platform-template-rust, and I've been hitting confusing setup issues and lower-level-than-expected errors. Is that work the best way for a higher-level programmer like me to help purity-inference along, or is there another way I can help it get into main / usable?
Can you post an example? Do you mean you found bugs in the code generation or confusing error messages?
I know getting the memory layouts right on the platform side is tricky, but I think that’s orthogonal to purity inference itself
Re getting on main, I’m planning to merge shortly but some tests seem to be failing on Linux despite the programs they run working fine outside of tests
It seems to be related to building a platform multiple times in parallel. I tried to fix it by building it only once and then using —prebuilt-platform
but that didn’t help. I’ll take another look tonight.
roc_std
in roc_host/
, which seems like a simple confusion that compounds everything else but also points to my general ignorance as to how packages are builtcargo build
froze with no error message here: https://github.com/lukewilliamboswell/roc-platform-template-rust/pull/2#issue-2636489209Stepping back a bit, I'm getting frustrated because I've been wanting to write a new Roc app by extending a basic platform, but I keep getting blocked by lower-level things like how platforms work behind the senes. It feels like we're in an in-between time where I shouldn't use main and I can't use purity-inference, but maybe I just need to start with main & basic-cli instead of a template?
Perhaps I've oversold my capacity to assist, since I'm only poking at this for like 30 minutes per day and don't feel like learning much that is tangential to my goals.
Note that we don’t have to migrate platforms to get the purity inference PR merged since it’s backwards compatible with Task
-based platforms.
You can’t mix and match in an app, but we can incrementally migrate platforms after merging
I don’t have a lot of free time now, so my focus is on getting the PR merged, but I’m happy to help with any platform efforts after
Sounds like maybe I should just use the nightly and build on basic-cli as-is!
Agus Zubiaga said:
Re getting on main, I’m planning to merge shortly but some tests seem to be failing on Linux despite the programs they run working fine outside of tests
Can we please ignore them. We've done a big uplift in the rebuild host PR and we will merge them in once purity inference lands.
Yeah, we could. It just occurred to me that I was testing the program on Linux arm while the tests run on x86. I’ll check that first just in case.
I'm trying out the purity inference branch with the Zig platform I'm working on. Really pleaseant experience so far, with great error messages.
I ran into a corner case. Suppose I have the following code:
foo : {} => U8
expect foo {} == 25
I get this warning:
── EFFECT IN TOP-LEVEL in platform/PagesInternal.roc ───────────────────────────
This call to PagesInternal.foo might produce an effect:
12│ expect foo {} == 25
^^^^^^
This is a weird example, in the real one that I simplified to this I have a higher-level function that supports running effectfully or not depending on the function passed in, and I have a couple of tests where I pass in pure functions. I don't think the warning should show under those circumstances (the test pass).
Also, I don't think code in an expect
is considered top-level, right?
To my understanding, we have avoided support for effect polymorphism, meaning that a function that takes in either a pure or effectful function will "assume" it takes an effectful function
For us to support that would entail exposing fx
variables in user code, which is messier than we want for now
Though in the future we may realize we get strong benefit from effect polymorphism, so the door isn't fully closed
All in all, I'd say this is expected behavior
Yeah, if you managed to write an effect polymorphic function, I’d be really interested to see it because that’d be considered a bug :laughing:
And yes, the expect
at the top-level considers its body as part of the "top-level" code
we probably need a specific error message for effects in expects
“top-level” might be confusing in that context
Eventually the plan is to remove expect-fx
and just have expect \get!, set! -> ...
, so we can probably improve the error messages then
But it would be an improvement to add that now, too
Yeah, that’ll come later
"add that now" meaning the top-level expect with effects message
Oh, I think I know where the confusion might come from. You are allowed to pass a pure function to something that expects an effectful function. However, the higher order function is still considered effectful.
You get warnings for things that don’t make sense like discarding the result of a pure function because that’s dead code. But otherwise, pure unifies with effectful.
and not the other way around, of course
Ah that makes sense, thanks.
I ran into something else. I've some functions that construct some build steps. Basically they take effectful functions and store them in a record without running them, and so are not effectul themselves (the compiler agrees). The record field needs a !
, so far so good. However, in some places I'm initializing that record with a pure function and I get a warning
Config : {
fn! : U8 => U8,
}
init : Config
init = {
fn!: \x -> x,
}
── UNNECESSARY EXCLAMATION in platform/PagesInternal.roc ───────────────────────
This field's value is a pure function, but its name suggests
otherwise:
20│ fn!: \x -> x,
^^^^^^^^^^^^
I can't take out the !
as the error suggests, because that would break the contract and constrain fn
to be effectful everywhere.
I managed to get rid of those earlier warnings I mentioned. I just had to manually created a pure version of the "polymorphic" function, use that version in tests, pull the common pure logic between the two (almost the entire function) into a helper. Pretty neat :heart_eyes:.
Jasper Woudenberg said:
── UNNECESSARY EXCLAMATION in platform/PagesInternal.roc ─────────────────────── This field's value is a pure function, but its name suggests otherwise: 20│ fn!: \x -> x, ^^^^^^^^^^^^
I can't take out the
!
as the error suggests, because that would break the contract and constrainfn
to be effectful everywhere.
Oh, that's a really good catch! I guess we only want this warning for record annotations, and destructuring, but not in record literals specifically.
we do want the opposite (MISSING EXCLAMATION) for record literals
I think I found another strange purity-inference related bug while upgrading roc-wasm4
https://github.com/lukewilliamboswell/roc-wasm4/pull/32#issuecomment-2481949480
I'm having trouble finding the source of this.
$ roc build ./examples/sound.roc --target=wasm32 --no-link
thread 'main' panicked at crates/compiler/mono/src/reset_reuse.rs:1269:42:
Expected symbol to have a layout. It should have been inserted in the environment already.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I see that all first-party platforms use pre-purity-inference Task-ful syntax. Do we/you want to intentionally defer all purity-inference update work until after AoC for stability and to prioritize responding to newcomer issues? Or do we/you hope to push basic-*
across the finish line by next week?
Just waiting on a review for https://github.com/roc-lang/roc/pull/7204
If we can merge that and get a new nightly... it's a clear path to a new purity-inference basic-cli
So could be on the streets early next week... folks can always use an older Task-based version of basic-cli if there are any issues with the new purity inference one, so we can keep it in pre-release until it's had a few more people test it out
In all but the most optimistic forecasts, does that mean we should prepare to keep post-purity-inference first-party platforms in pre-release mode until after AoC?
If not, then the tutorial and documentation seem unready to assist newcomers with that, conceptually or snippetly. https://www.roc-lang.org/tutorial#tasks
Basically, I'm expecting someone to announce a month-long AoC freeze on breaking changes to @roc-lang, for UX.
I'm hoping we can switch over to the release management process we've discuss/agreed on
Basically, we can make breaking changes to roc but the supported platforms and recommended version for everything stays stable/relibable.
Does anyone want to have a crack at updating the Tutorial for purity inference? Not a major uplift, just removing Task
Shouldn't such an update also add explainers for !
, =>
, and try
?
Sounds like you're halfway there @JanCVanB :grinning_face_with_smiling_eyes:
Hehe but I still don't fully understand where Tasks went so I'm hesitant to explain anything!
(Side note, the tutorial and linked examples still use ?
, which seems maybe deprecated/removed?)
I'm happy to give it a try tomorrow, partly as an exercise in outlining my own confusions!
With such inspiring sentences as "I infer that try
bubbles any downstream Err
tags up all the way to the platform, though you can no longer see the tags in function signature types... hmm that doesn't seem right..."
Edit: yay I'm wrong on the "no longer see" part
Getting started is the hard part :smiley: I'm sure we can help to find the right words. It would be very helpful to sit back and think about the new things (syntax etc) and map out what is missing or needs adding.
I'm not thinking we should add/update everything, but we could do a pass or two and polish it a little so newcomers can still follow along and have a good experience.
As a returning user, I'm concerned that in the last month or two Roc has frontloaded its complexity (slightly steepened its learning curve to greatly improve code readability) without yet paying the cost in documentation or newcomer pushback... right before AoC.
JanCVanB said:
Hehe but I still don't fully understand where Tasks went so I'm hesitant to explain anything!
Tasks are still functional, we just decide whether to use the old style of Tasks or the new style of purity inference based on the following three rules:
? is still present, but will be removed at least for now, since it desugars with Result.try
instead of the try
keyword with early returns
So it's still in the language, but in a month or so it's probably gone. Basically, whenever we remove tasks
True, but if in the next week we ensure that every AoC-friendly platform requires main!
, then to a newcomer the concept of tasks is fully implicit and the tutorial should never capitalize the word.
The try
keyword is much simpler to understand now IMO, since it just does what Rust does. it desugars to
when thingYouTried and the args is
Ok val -> val
Err err ->
return Err err
I can't think of a reason the word Task, uppercase or lowercase, should be preserved at all in the tutorial. Though I'm open to being wrong
In that example, does the type of err
appear in the try
ing function's type signature?
Yes
I may be being overly pessimistic (or exhibiting an "old dog new tricks" stiffness). Also, AoC is both an opportunity for adoption and an opportunity for constructive feedback on Roc's recent choices.
So for example
parseThenDoubleAndPrint! = \strNum ->
num = try Str.toU64 strNum
doubled = num * 2
try Stdout.line! "$(Inspect.toStr doubled)"
Ok {}
Would infer type Str => Result {} [InvalidNumStr, StdoutErr _]
Sweet! Like this? https://github.com/roc-lang/basic-cli/pull/257#discussion_r1853325645 (comment on line 11)
In other words, jinx, cause I forgot to hit submit on the PR review until now.
Yes, but replace the a with the actual error type
Also, though task still exists, it is deprecated and will be removed once purity inference is fully working and things are updated.
Just a tracking issue for my in-progress tutorial work: https://github.com/roc-lang/roc/issues/7242
found a bug
If a platform exposes a function like respond!
and the implementation in the app uses no effects, it generates a warning
It is valid for the app to implement respond!
with a simple static response that uses no effects
So we should not check the !
effectfulness over the platform to app boundary
Yeah, I noticed that as well. We could add a constraint to all top-level defs to allow them to be effectful without an effectful body?
I think it would be best if we make a special exception just for the platform
Other locations this shouldn't happen
Yeah, good catch. It should definitely be only for functions provided to the platform.
Top-level defs are the main motivation for this warning since those are the functions you can call from far away. Disabling it for all of them would kind of defeat the purpose.
Yeah, I think it should just be an exception for the functions specified in the provides.
I’ll look into it tonight
Fixed in #7254
Should this file of Result parsing/trying examples (linked from the tutorial) include versions using the new try
keyword?
CC @Anton @Aurélien Geron
Yes - that would be good :thumbs_up:
Tutorial PR mostly finished - final additions coming tomorrow, ready for primary feedback whenever, hoping to merge tomorrow
https://github.com/roc-lang/roc/pull/7274
(TODO: purity inference previews, Result examples file additions)
JanCVanB said:
Should this file of Result parsing/trying examples (linked from the tutorial) include versions using the new
try
keyword?
CC Anton Aurélien Geron
Thanks for the heads up. I'm on vacation without my laptop, I'll be back home on Thursday. If it can wait until then, I'm happy to update the example. :+1:
Last updated: Jul 05 2025 at 12:14 UTC