what does ! behind an function mean
It means that a function is effectful, or that is does stuff outside of the function.
The function returns a task, or soon, that it is an impure function - which semantically means something similar
thanks, is a task smth i have to unwrap like an Result
For example, readFileBytes! : Str => List U8
won't always return the same thing because it reads a file from the "outside world" aka your filesystem
So... awkwardly, Roc is in between two effectful ! worlds right now
The old one is Tasks, which are basically a Task ok err := {} -> Result ok err
"thunk"
Those need to be wrapped like Result, yes
The new world is just purity inference, which is where normal, "pure" functions are basically the exact same as effectful functions, except we enforce that functions that "do stuff outside" have an ! at the end of their name
For the next month or two, the first one is what you'll be using, meaning you need to return Task.ok yourValue
instead of just Ok yourValue
But in the future, it'll be just Ok yourValue
I think it's well explained in the tutorial: https://www.roc-lang.org/tutorial#the-postfix-await-operator
basically
main =
Stdout.line! "hello"
Stdout.line! "word" # note, this ! can be omitted and the desugaring is the same (because it's the last one before returning)
becomes
main =
Task.await (Stdout.line "hello") \_ ->
Stdout.line "world"
Yep! That's exactly what's happening right now.
does it mean the api will change to Result instead of Task?
It can be a Result, it can also not be a Result
oh
ok
Something like readFileBytes! : Str => Result (List U8) ReadErr
will use a Result because it might fail
But something like envVars! : {} => Dict Str Str
won't because it can't fail
i have primarily experience with Rust and Ocaml
Sam Mohr said:
Something like
readFileBytes! : Str => Result (List U8) ReadErr
will use a Result because it might fail
does it mean (or will it mean?) that without the !
:
readFileBytes : Str -> Task (Result (List U8) ReadErr) [SomeOtherErr]
?
and what is the fat arrow =>
?
I'm missing a bunch of updates :D
We're in between a few different "shapes" of Roc, so we haven't been communicating everything very clearly
The current effectful Roc with Task
has two types parameters, ok
and err
, since it always returns a Result under the hood
So it'd be readFileBytes : Str -> Task (List U8) ReadErr
The =>
implies that a function is effectful. The name ending in ! also implies it. It's arguably redundant, but there are some cases with nested type signatures where you need =>
for clarity
I see so in this potential future the !
would be "in the function name" so to speak? and it would have the same meaning of a =>
that is: this function may return a different value even if input do not change?
Basically the !
is to tell you - without seeing the types - that an effect is happening
The =>
tells you an effect is happening without seeing the implementation
@Sam Mohr I updated the tutorial recently to explain both the current implementation of !
as operator and the upcoming-but-not-ready-yet switch to a suffix. I hope that the new Under Construction section of the tutorial provides a sufficient first exposure of these concepts!
I think this month we shouldn't mention purity inference concepts/changes to anyone who isn't specifically asking about them. Roc has enough mental overhead for newcomers with the features that have already landed!
Last updated: Jul 06 2025 at 12:14 UTC