Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type annotations for suffixed sugar #6868

Merged
merged 12 commits into from
Jul 24, 2024

Conversation

kdziamura
Copy link
Collaborator

@kdziamura kdziamura commented Jul 4, 2024

From Zulip thread

In this implementation, type annotations are propagated this way:

Annotated assignments:

f = \x ->
    r : A
    r = x!
    Task.ok r

f = \x ->
    Task.await
        (
            # Type annotation goes here as the first Task arg
            a0 : Task A _
            a0 = x 
            a0
        )
        \r -> Task.ok r

Statements:

f = \x ->
    x!
    Task.ok {}

f = \x ->
    # Stmt desugared to AnnotatedBody with unit type
    _ : {}
    _ = x!
    Task.ok {}

f = \x ->
    Task.await
        (
            # Type annotation goes here as the first Task arg
            a0 : Task {} _
            a0 = x
            a0
        )
        \_ -> Task.ok {}

@kdziamura kdziamura self-assigned this Jul 4, 2024
@kdziamura kdziamura changed the title Suffixed stmt desugaring Fix suffixed stmt desugaring Jul 4, 2024
@kdziamura kdziamura enabled auto-merge July 4, 2024 17:08
@lukewilliamboswell
Copy link
Collaborator

lukewilliamboswell commented Jul 5, 2024

@kdziamura -- I've added to the zulip discussion seeking clarification on the intended design.

I think the current behaviour is correct as it restricts the use of statements to Task {} _, while the proposed change would permit using a task that returns anything and is not correct.

If a user would like to ignore the output they can be explicit and use a _ = assignment, otherwise this should be a type error.

Does this explanation help with your original problem?

@kdziamura
Copy link
Collaborator Author

kdziamura commented Jul 5, 2024

It makes sense. I'll change _ back to {}. The other part of the fix seems to be correct:

ignoreResult = \task ->
    Task.await (Task.result task) \{} ->
        Task.ok {}

# 3. drop destructuring because input and output are the same. it's incorrect
ignoreResult = \task ->
    Task.result task

this step hides the intention of returning Task.ok {}, so the actual fix is to drop it

@kdziamura
Copy link
Collaborator Author

kdziamura commented Jul 5, 2024

@lukewilliamboswell I updated the fix. Now it works as expected and generates a type error:

ignoreResult = \task ->
    Task.result! task
    Task.ok {}
── TYPE MISMATCH in Bug.roc ────────────────────────────────────────────────────

This 2nd argument to this function has an unexpected type:

8│      Task.result! task
                    ^^^^^

The argument is an anonymous function of type:

    {}a -> Task {} *

But this function needs its 2nd argument to be:

    Result ok err -> Task {} *

@kdziamura kdziamura marked this pull request as draft July 5, 2024 15:16
auto-merge was automatically disabled July 5, 2024 15:16

Pull request was converted to draft

@kdziamura kdziamura force-pushed the suffixed-stmt-desugaring branch 3 times, most recently from d0155ea to a76f987 Compare July 8, 2024 19:42
@kdziamura kdziamura changed the title Fix suffixed stmt desugaring Respect type annotations in suffixed statements Jul 8, 2024
@kdziamura kdziamura changed the title Respect type annotations in suffixed statements Respect type annotations for suffixed statements Jul 8, 2024
@kdziamura kdziamura changed the title Respect type annotations for suffixed statements wip: Respect type annotations for suffixed statements Jul 8, 2024
@kdziamura kdziamura changed the title wip: Respect type annotations for suffixed statements Respect type annotations for suffixed statements Jul 9, 2024
@kdziamura kdziamura marked this pull request as ready for review July 9, 2024 17:08
@kdziamura kdziamura marked this pull request as ready for review July 16, 2024 09:51
@kdziamura kdziamura changed the title Respect type annotations for suffixed statements Type annotations for suffixed sugar Jul 16, 2024
// _ = stmt_expr!

let region = stmt_expr.region;
let new_pat = arena.alloc(Loc::at(region, Pattern::Underscore("#!stmt")));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these patterns ever conflict with each other?

Copy link
Collaborator Author

@kdziamura kdziamura Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. At least not with the current design. The underscore pattern is not an identifier and it has no unique name restriction

/// Suffixed sub expression
/// e.g. x = first! (second! 42)
/// In this example, the second unwrap (after unwrapping the top level `first!`) will produce
/// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!a0, sub_new: #!a0 }>`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit. We change the unique prefix above, so these comments are now not quite right.

Otherwise these comments are a helpful addition.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I updated the other comments, seems to miss this one

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 3b50a83

@@ -60,19 +70,17 @@ fn init_unwrapped_err<'a>(
// Provide an intermediate answer expression and pattern when unwrapping a
// (sub) expression.
// e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0`
let answer_ident = arena.alloc(next_unique_suffixed_ident());
let ident = arena.alloc(format!("{}_arg", next_unique_suffixed_ident()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we add the _arg?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for clearer purpose of the variable. The value comes from the argument

Defs(arena.alloc(defs), new_var),
))
}
_ => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it clearer if this is None?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, will change

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 3b50a83

Copy link
Collaborator

@lukewilliamboswell lukewilliamboswell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good, and I've done some testing also.

The only thing I'm unsure of is the #!stmt's and if they should also have unique identifiers/numbers with them?

Also we are switching in comments between the new and old syntax. Might be helpful to do a find-all and replace the old versions.

@kdziamura kdziamura force-pushed the suffixed-stmt-desugaring branch 2 times, most recently from 80c99c1 to 3b50a83 Compare July 22, 2024 18:04
@kdziamura kdziamura enabled auto-merge July 22, 2024 19:11
let new_ident = match loc_pat.value {
Pattern::Underscore("#!stmt") => format!("{}_stmt", new_ident),
Pattern::Identifier { ident }
if ident.starts_with('#') && ident.ends_with("_stmt") =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a little fishy; why do we need to do string parsing/matching like this? This is mildly concerning since it introduces a very subtle dependency, where someone changing a string that shouldn't matter, can accidentally have broader consequences in the future.

Is there a way to get this same information without trying to parse identifier strings like this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Need to change the existing enum structure or introduce another one. I just didn't want to have too many changes in this pr to keep it simple. I can refactor it as a follow-up

use roc_types::types::AnnotationSource::*;

let is_suffixed = match &name.value {
Pattern::Identifier(symbol) => symbol.as_str(alloc.interns).starts_with("#!"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here - is there a way that we can get the info we need here, without inspecting a string like this?

@kdziamura kdziamura merged commit d5db313 into roc-lang:main Jul 24, 2024
17 checks passed
Anton-4 added a commit to roc-lang/basic-cli that referenced this pull request Jul 24, 2024
@kdziamura kdziamura deleted the suffixed-stmt-desugaring branch July 29, 2024 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants