roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
4.38k stars 309 forks source link

request: novice-readable error message for task/function mixups #6580

Open gvwilson opened 7 months ago

gvwilson commented 7 months ago

Working through the Roc tutorial, I wanted to write a function that printed things, so I tried this:

app "example"
    packages { cli: "https://github.com/roc-lang/basic-cli/releases/download/0.8.1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA.tar.br" }
    imports [cli.Stdout]
    provides [main] to cli

oneLine = \{} ->
    Stdout.line "first"

main =
    oneLine

roc dev example.roc produced this:

── TYPE MISMATCH in ....1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA/main.roc ─

Something is off with the type annotation of the main required symbol:

2│      requires {} { main : Task {} I32 }
                             ^^^^^^^^^^^

This #UserApp.main value is a:

    ({}a -> Task.Task {} *)

But the type annotation on main says it should be:

    InternalTask.Task {} I32

Tip: Type comparisons between an opaque type are only ever equal if
both types are the same opaque type. Did you mean to create an opaque
type by wrapping it? If I have an opaque type Age := U32 I can create
an instance of this opaque type by doing @Age 23.

── TYPE MISMATCH in ....1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA/main.roc ─

Something is off with the body of the mainForHost definition:

26│  mainForHost : Task {} I32 as Fx
27│  mainForHost = main
                   ^^^^

This #UserApp.main value is a:

    ({}a -> Task.Task {} *)

But the type annotation on mainForHost says it should be:

    InternalTask.Task {} I32

Tip: Type comparisons between an opaque type are only ever equal if
both types are the same opaque type. Did you mean to create an opaque
type by wrapping it? If I have an opaque type Age := U32 I can create
an instance of this opaque type by doing @Age 23.

────────────────────────────────────────────────────────────────────────────────

2 errors and 0 warnings found in 25 ms.

You can run the program anyway with roc run example.roc

I believe a lot of novices will assume Stdout.line is Roc's equivalent of print and will then put it in their functions. They won't yet have seen tasks, so the error message above will be incomprehensible. (I had to read quite far ahead in the Roc tutorial and look at a couple of examples in order to start to understand the problem.) While I don't often ask for error messages to be shortened, I think this might be a good candidate. Alternatively, can some other form of output be introduced earlier so that people don't even try to do what I did?

Anton-4 commented 7 months ago

Thanks @gvwilson, this is great feedback :)

I've talked about a tool to make it easier for beginners and novices to prevent these types of errors on zulip. What do you think about a tool like that? If it works well we can try to include it in the compiler.

gvwilson commented 7 months ago

Added a note on zulip: "I can see the value of the tool that @Anton-4 suggests, but I think that better/slimmer error messages should be the priority. In particular, I think what I'm seeing (as a novice) is cascading error messages: if there's a way to say, "we spotted four other problems but we think they're all related to the one we showed you first", that would be very helpful for newcomers."