Stream: beginners

Topic: ✔ Trying to figure out a straightforward function annotation


view this post on Zulip Hristo (Mar 08 2024 at 18:21):

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!

view this post on Zulip Anton (Mar 08 2024 at 18:48):

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.

view this post on Zulip Anton (Mar 08 2024 at 18:59):

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.

view this post on Zulip Hristo (Mar 08 2024 at 19:05):

Anton said:

But you will then encounter problems here with the type of 1 (in c == 1).

Yes, precisely, I did run into that as well.

Anyway, the easy fix is to use U64 or I64 instead of Num *. 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.

view this post on Zulip Notification Bot (Mar 08 2024 at 19:05):

Hristo has marked this topic as resolved.

view this post on Zulip Anton (Mar 08 2024 at 19:14):

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.

view this post on Zulip Brendan Hansknecht (Mar 08 2024 at 23:58):

Why is there a problem with c == 1? That seems like it should work

view this post on Zulip Richard Feldman (Mar 09 2024 at 00:18):

the problem is that the type annotation says Num * rather than Int *

view this post on Zulip Richard Feldman (Mar 09 2024 at 00:19):

for the type of c

view this post on Zulip Richard Feldman (Mar 09 2024 at 00:19):

Num * doesn't have Eq because Num * can include floats

view this post on Zulip Richard Feldman (Mar 09 2024 at 00:19):

whereas Int * does have Eq because all ints have Eq

view this post on Zulip Brendan Hansknecht (Mar 09 2024 at 01:21):

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)"

view this post on Zulip Richard Feldman (Mar 09 2024 at 01:43):

unfortunately not, because a doesn't really relate to Num itself

view this post on Zulip Richard Feldman (Mar 09 2024 at 01:43):

it's just a type marker like Integer Signed32 or something like that

view this post on Zulip Richard Feldman (Mar 09 2024 at 01:43):

so this is saying that "whatever type marker you use has Eq" but that's not helpful here :big_smile:

view this post on Zulip Brendan Hansknecht (Mar 09 2024 at 03:03):

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

view this post on Zulip Brendan Hansknecht (Mar 09 2024 at 03:04):

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)

view this post on Zulip Hristo (Mar 09 2024 at 09:20):

Brendan Hansknecht said:

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

Exactly, @Brendan Hansknecht! That's where I was trying to pull it towards (the first option you mentioned).

view this post on Zulip Richard Feldman (Mar 09 2024 at 12:37):

yeah I guess the difference between that and Int * is whether Dec is accepted


Last updated: Jul 06 2025 at 12:14 UTC