I've tried to be very explicit for the compiler, with hints to use (Int Unsigned32), and have even added a bunch of Num.toU32 throughout my code. Yet, it's still finding an issue. I'm not sure how to continue debugging this.
The very odd thing to me is this fixes it.
copies = 0
This breaks the Dict type:
copies = Dict.get numCardsByGame index |> Result.withDefault 0
Error message:
This 2nd argument to |> has an unexpected type:
51│ |> incrementCards numCardsByGame
^^^^^^^^^^^^^^
This numCardsByGame value is a:
Dict (Int Natural) (Int Natural)
But |> needs its 2nd argument to be:
Dict (Int Unsigned32) (Int Unsigned32)
The full code section:
AoC code inside
Ah, I see... It's that the copies
variable needs to be Int Natural
given how it's used later. Then, following that backwards, we see that Dict.get numCardsByGame index...
must be able to return a Int Natural
.
Jason Hobbs has marked this topic as resolved.
Jason Hobbs has marked this topic as unresolved.
I think my understanding about "following that backwards" is wrong.
Appending |> Num.toNat
to the Dict.get
should've resolved the need for Dict.get
to return an Int Natural
. Especially, since I gave it the starting type emptyDict : Dict (Int Unsigned32) (Int Unsigned32)
Okay, that did resolve half of it... I just didn't realize the same thing was happening via the index
I was using.
Got there...
copies : Nat
copies = Dict.get numCardsByGame (Num.toU32 index) |> Result.withDefault 0 |> Num.toNat
Jason Hobbs has marked this topic as resolved.
the fun of integer types.... Nat being an especially common pain point due to suddenly changing the api and leading to type mismatch.
Yeah, I don't really know how to troubleshoot these very well yet.
Right now, it's a lot of trial and error toggling compiler hints and casting inline to get more info.
Probably a sign that our errors aren't great here due to unifying wrong and then they type getting caught elsewhere
A lot fo time the main way to help with this is to turn lambdas into real functions and type them. Then if needed, also type some variables. That gives concretes for the roc type checker to latch onto and propagate to catch the errors more locally.
I love the term pinning the types at that point, I heard it here somewhere
Yeah, pinning is a great term for that. I wonder if we can add this to a guide somewhere. Just seams like a useful concept for many
Pinning has a specific meaning in erlang/elixir, but this wouldn't be the first time a software word is used to mean two different things https://hexdocs.pm/elixir/1.16/pattern-matching.html#the-pin-operator
I guess the less obscure place pinning is used would be version pinning in some package managers.
Rust also has its pinning: https://doc.rust-lang.org/std/pin/index.html ;]
@Richard Feldman would you be ok with a couple of sentences added to tutorial#type-annotations using this term? I think this is a really natural analogy for the interaction with the compiler being optional, and how you want to add annotations because it helps the compiler help you.
When I think of pinning, I normally think of pinning memory for gpu related work. but I think the term is generic enough we can use it here too.
Last updated: Jul 06 2025 at 12:14 UTC