I've been playing with function annotations, as part of my own approaches to learning Roc (I'm essentially translating a bunch of my own Elm code samples to Roc), and as a related side exploration, I've run into the following, when trying to annotate this example from Roc's tutorial:
The rockin' roc repl
────────────────────────
Enter an expression, or :help, or :q to quit.
» pl = \s, p, c ->
… cs = Num.toStr c
… if c == 1 then
… "$(cs) $(s)"
… else
… "$(cs) $(p)"
<function> : Str, Str, Num * -> Str
So, it's all OK up until this point.
But then:
» pl : Str, Str, Num * -> Str
… pl = \s, p, c ->
… cs = Num.toStr c
… if c == 1 then
… "$(cs) $(s)"
… else
… "$(cs) $(p)"
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
This expression has a type that does not implement the abilities it's expected to:
7│ if c == 1 then
^
I can't generate an implementation of the Eq ability for
Num *
In particular, an implementation for
*
cannot be generated.
Tip: This type variable is not bound to Eq. Consider adding an
implements clause to bind the type variable, like
where * implements Bool.Eq
and even further:
» pl : Str, Str, Num * -> Str where * implements Eq
… pl = \s, p, c ->
… cs = Num.toStr c
… if c == 1 then
… "$(cs) $(s)"
… else
… "$(cs) $(p)"
…
» pl "cactus" "cacti" 3
── NOT END OF FILE ─────────────────────────────────────────────────────────────
I expected to reach the end of the file, but got stuck here:
3│ pl : Str, Str, Num * -> Str where * implements Eq
^
I believe this should be a straightforward thing to figure out, but all my attempts have failed.
Essentially, without the type annotation, the function does work as expected. But I can't seem to annotate it properly (tried with Bool.Eq
etc), despite all the help from the compiler.
Thank you for your time!
I don't think we support *
with where ... implements
. So in general you should use a specific type variable like a
.
But you will then encounter problems here with the type of 1
(in c == 1
).
Anyway, the easy fix is to use U64
or I64
instead of Num *
. I'll file some issues to improve the experience here.
Sidenote: the reason you need to add where a implements Eq
here is because the number could be a floating number. Floats don't support Eq
, because you need to be cautious when comparing floats. To compare floats we have isApproxEq
.
Anton said:
But you will then encounter problems here with the type of
1
(inc == 1
).
Yes, precisely, I did run into that as well.
Anyway, the easy fix is to use
U64
orI64
instead ofNum *
. I'll file some issues to improve the experience here.
Thank you!:pray:
What did confuse me in this case was the fact that the inferred function annotation wasn't practically applicable.
I'll mark this thread as resolved.
Hristo has marked this topic as resolved.
What did confuse me in this case was the fact that the inferred function annotation wasn't practically applicable.
Yeah, that should not happen, I expect that may be hard to fix but I'll make an issue.
Why is there a problem with c == 1
? That seems like it should work
the problem is that the type annotation says Num *
rather than Int *
for the type of c
Num *
doesn't have Eq
because Num *
can include floats
whereas Int *
does have Eq
because all ints have Eq
This should work though, right?
» pl : Str, Str, Num a -> Str where a implements Eq
… pl = \s, p, c ->
… cs = Num.toStr c
… if c == 1 then
… "$(cs) $(s)"
… else
… "$(cs) $(p)"
unfortunately not, because a
doesn't really relate to Num
itself
it's just a type marker like Integer Signed32
or something like that
so this is saying that "whatever type marker you use has Eq
" but that's not helpful here :big_smile:
Ah, so I really need where (Num a) implements Eq
. Which of course is an unsupported syntax
Or would need something like a where a implements Eq & NumericOps
or something like that
Of course neither being supported though the second could technically be implemented as a builtin if we really wanted (and would probably be added if we ever did custom infix ops for numerics)
Brendan Hansknecht said:
Ah, so I really need
where (Num a) implements Eq
. Which of course is an unsupported syntaxOr would need something like
a where a implements Eq & NumericOps
or something like that
Exactly, @Brendan Hansknecht! That's where I was trying to pull it towards (the first option you mentioned).
yeah I guess the difference between that and Int *
is whether Dec
is accepted
Last updated: Jul 06 2025 at 12:14 UTC