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: Nov 09 2025 at 12:14 UTC