I have run into what appears to be a bug with type resolution when passing multiple arguments containing Task functions into another function - from what I can tell, the compiler is having trouble doing error accumulation across these multiple arguments.
I am attempting to adapt a function from:
dispatchToolCalls : List ToolCall, Dict Str (Str -> Task Str *) -> Task (List Message) _
dispatchToolCalls = \toolCallList, toolHandlerMap -> #...
To:
dispatchToolCalls : List ToolCall, Dict Str (Str -> Task Str *), { logger ? Str -> Task {} * } -> Task (List Message) _
dispatchToolCalls = \toolCallList, toolHandlerMap, { logger ? \_ -> Task.ok {} } -> #...
logger
causes an error. ── TYPE MISMATCH in ../package/Tools.roc ────────────────────
This 2nd argument to this function has an unexpected type:
117│ logger! "Calling tool: $(toolName)"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The argument is an anonymous function of type:
{} -> Task […] *
But this function needs its 2nd argument to be:
{} -> Task […] *
Tip: Any connection between types must use a named type variable, not
a *!
It seems to me like this should be a viable type signature, and that the underscore should be able to accumulate the types of the Dict
tasks as well as the logger. Is there a correct way to write this type signature that I am missing? Am I mistaken in believing this should be possible?
Can you share your code? It doesn't have to be a minimal reproduction
Yeah, let me commit and push a new branch.
package/Tools.roc
dispatchToolCallsLogged : List ToolCall, Dict Str (Str -> Task Str *), (Str -> Task {} *) -> Task (List Message) _
dispatchToolCallsLogged = \toolCallList, toolHandlerMap, logger ->
Task.loop { toolCalls: toolCallList, toolMessages: [] } \{ toolCalls, toolMessages } ->
when List.first toolCalls is
Ok toolCall ->
toolName = toolCall.function.name
when toolHandlerMap |> Dict.get toolCall.function.name is
Ok handler ->
logger! "Calling tool: $(toolName)"
toolMessage = callTool! toolCall handler
updatedToolMessages = List.append toolMessages toolMessage
Task.ok (Step { toolCalls: (List.dropFirst toolCalls 1), toolMessages: updatedToolMessages })
_ ->
logger! "Couldn't find tool: $(toolName)"
Task.ok (Step { toolCalls: (List.dropFirst toolCalls 1), toolMessages })
Err ListWasEmpty -> Task.ok (Done toolMessages)
https://github.com/imclerran/roc-ai/tree/add-tool-logging
I'll take a look
Your roc-lang badge on the repo is pretty cool
This works:
dispatchToolCallsLogged : List ToolCall, Dict Str (Str -> Task Str a), (Str -> Task {} a) -> Task (List Message) _
I can't completely explain why though given everything in the "important details" section
The details of error accumulation can be quite tricky
I assume it's the "any connection" in the tip, the toolHandlerMap
and logger
are connected through error accumulation
Tip: Any connection between types must use a named type variable, not
a *!
This could also be happening: two *
in one definition means that these two types can not be the same, while in truth they could actually be the same.
Hmm.. thats really weird! I tried a bunch of combinations of type variables, etc. I'm picking up on this after not having touched the bug out of frustration in a few weeks, so some of my troubleshooting happened a while ago. Would have sworn I tried using matched type variables, but maybe I only used something like:
dispatchToolCallsLogged : List ToolCall, Dict Str (Str -> Task Str a), (Str -> Task {} b) -> Task (List Message) _
PS: Feel free to grab the roc badge!
I'm going to adapt handleToolCalls accordingly, and see if this actually works in practice when used in an example where the type of logger is actually defined, but I imagine if it type checks here, it should still typecheck fine.
Hmm.. well it type checks just fine, but when I pass Stdout.line into logger, nothing is ever printed, despite tools clearly being called by the program. So thats a weird, maybe, but probably not related bug. And If I leave the record empty to use the default, I get a "no lambda set found" error message:
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: <https://github.com/roc-lang/roc/issues/new/choose>
no lambda set found for (`ai.Tools.IdentId(47)`, []): LambdaSet {
set: [
( ai.Tools.48, []),
],
args: [
InLayout(STR),
],
ret: InLayout(
290,
),
representation: InLayout(
291,
),
full_layout: InLayout(
292,
),
}
Location: crates/compiler/mono/src/layout.rs:1590:17
Last updated: Jul 05 2025 at 12:14 UTC