Hi all, was recently migrating a codebase to support the latest roc and basic-cli versions, and I was using a Task.loop call in my code.
I got a strange error message from my Task.loop usage:
Something is off with the body of this suffixed statement:
20│ Task.loop! { client, previousMessages: initializeMessages } loop
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This loop call produces:
Task {
client : Client.Client,
previousMessages : List Chat.Message,
} […]
But a suffixed statement is expected to resolve to an empty record:
Task {} […]
By changing my loop function to return an empty record when Done
, I was able to resolve the issue. However, I see that the Task.loop
function signature has not changed in the documentation. Is this a bug, or am I misunderstanding some changes to how Task.loop
works now?
Update: apparently, this error message is caused because I was not capturing the return value from Task.loop
. This seems like a problem, as one might want to write a loop function which sometimes you care about capturing its return value, and sometimes you don't. There should be an option to call Task.loop!
without assigning its value, even if the function passed to Task.loop
does include a non-empty record in its tagged Done {}a
return.
Yeah, lone !
statements are only allowed with fully ignorable values. That is {}
.
If the Done value wraps {}
, it should work as a lone statement with !
@Ian McLerran can I see the source?
Yeah, no problem. I'll grab it.
Full source here, and a snippet of the source here:
main =
apiKey = getApiKey!
model = getModelChoice!
providerOrder = Dict.get preferredProviders model |> Result.withDefault []
client = Chat.initClient { apiKey, model, providerOrder }
Stdout.line! "Using model: $(model |> Ansi.color { fg: Standard Magenta })\n"
Stdout.line! "Enter your questions below, or type 'Goodbye' to exit"
Task.loop! { client, previousMessages: initializeMessages } loop # <--- error here ---
Stdout.line (colorizeRole (Assistant "\nAssistant: I have been a good chatbot. Goodbye! 😊"))
loop = \{ client, previousMessages } ->
Stdout.write! "You: "
query = Stdin.line!
messages = Chat.appendUserMessage previousMessages query
when query |> strToLower is
"goodbye" -> Task.ok (Done { previousMessages }) # <--- Done {}
"goodbye." -> Task.ok (Done { previousMessages }) # <--- Done {}
"goodbye!" -> Task.ok (Done { previousMessages }) # <--- Done {}
_ -> handlePrompt client messages
And what is handlePrompt
?
But yeah, from this at first look, if those returned Done {}
, I would expect that Task.loop!
to function
Cause that should lead to Task.loop
returning Task {} err
Might be worth adding a type loop
to makes sure it is returning a Done {}
.
Obviously if you return Done { previousMessages }
, it will require _ = Task.loop!
, but with changing to Done {}
, it looks all good assuming handlePrompt
is all good as well.
There is a small chance we implemented something wrong in the standard library that is messing up types here, but I doubt it.
Ian McLerran has marked this topic as resolved.
Brendan Hansknecht said:
But yeah, from this at first look, if those returned
Done {}
, I would expect thatTask.loop!
to function
Oh yeah, sorry, replacing Done { previousMessages }
with Done {}
does work. I should have remembered I could just assign the result to a _
to resolve that error, and keep my function definition unchanged.
Last updated: Jul 06 2025 at 12:14 UTC