Skip to content

Remove invalid unwrap annotation#138

Open
dillonkearns wants to merge 5 commits intomdgriffith:mainfrom
dillonkearns:fix/unwrap-annotation
Open

Remove invalid unwrap annotation#138
dillonkearns wants to merge 5 commits intomdgriffith:mainfrom
dillonkearns:fix/unwrap-annotation

Conversation

@dillonkearns
Copy link
Copy Markdown
Contributor

Say we are writing codegen for a type like:

type MyType = Wrapper String

Elm.unwrap and Elm.unwrapper generate a lambda that pattern matches on a single-variant custom type:

 \(Wrapper val) -> val

The argument pattern matches on Wrapper, so the argument type should be the Wrapper variant's type, not a generic type variable (Elm gives an error if you use a generic type variable):

Before:

extract : val -> unwrapped
extract (Wrapper val) =
    val
TYPE MISMATCH: The argument is `MyType`, but the type annotation
says `val`.

Since we don't know the type name (MyType in the example I gave above) we can't infer it so we short-circuit the type annotation by returning Err [].

After:

extract (Wrapper val) =
    val

If you know the types, pipe through Elm.withType to attach them:

Elm.unwrapper [] "Wrapper" |> Elm.withType (Type.function [Type.named [] "MyType"] Type.string)

Returning Err [] from unwrapper would have lost inference for outer expressions too, since applyType used to short-circuit on any Err. For example, Elm.Op.plus (Elm.int 1) (Elm.unwrap [] "Wrapper" wrapped) would produce no signature at all. So applyType now distinguishes Err [] (intentionally unknown) from Err [errors] (an actual inference failure). When the function annotation is Err [], it synthesizes a fresh generic return type so downstream inference still flows, and the outer declaration still gets foo : Int.

If you would prefer to do a bigger refactor to have a custom type to represent an unknown inference value instead of having special meaning for Err [], we could totally do that too, just let me know!

dillonkearns and others added 5 commits April 10, 2026 11:28
Elm.unwrapper generates a lambda that pattern matches on a
single-variant custom type:

    \(Wrapper val) -> val

The previous annotation was `val -> unwrapped` — two generic type
variables. This annotation doesn't compile in Elm: the lambda body
extracts a concrete value (whatever `Wrapper` wraps), but the
annotation says ANY type, so Elm rejects it with a TYPE MISMATCH.

Any annotation we could generate would have the same problem,
because unwrapper doesn't know the inner type of the custom type
from just the name.

The fix is to omit the annotation entirely, letting Elm infer the
type from the custom type definition.

Before:
    extract : val -> unwrapped
    extract (Wrapper val) =
        val

    -- TYPE MISMATCH: `val` is `Int`, but the annotation says `unwrapped`

After:
    extract (Wrapper val) =
        val

    -- Elm infers: extract : Wrapper -> Int

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a function's annotation is `Err []` (intentionally unknown, e.g.
Elm.unwrapper's lambda), `applyType` now synthesizes a fresh generic
return type instead of short-circuiting to `Err`. This restores
inference in expressions like `1 + Elm.unwrap "Wrapper" wrapped`,
which previously lost their signature when the unwrap bug was fixed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Became dead when unwrapper's annotation switched to `Err []`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`apply fn []` with an unknown fn type should still be unknown (the
result is the fn itself, whose type we still don't know), rather
than gaining a synthesized return type out of thin air.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dillonkearns dillonkearns force-pushed the fix/unwrap-annotation branch from 49028d3 to 7af311a Compare April 22, 2026 16:41
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.

1 participant