Skip to content

Fix overly general typeclass in generated type annotations#133

Open
dillonkearns wants to merge 1 commit intomdgriffith:mainfrom
dillonkearns:fix/typeclass-constraints
Open

Fix overly general typeclass in generated type annotations#133
dillonkearns wants to merge 1 commit intomdgriffith:mainfrom
dillonkearns:fix/typeclass-constraints

Conversation

@dillonkearns
Copy link
Copy Markdown
Contributor

Functions using +, <, or ++ were incorrectly resulting in type annotations with plain type variables instead of constrained ones, producing code that doesn't compile.

Example

Elm.Declare.fn2 "addBoth"
    (Arg.var "a")
    (Arg.var "b")
    (\a b -> Elm.Op.plus a b)

Before (broken)

addBoth : a -> a -> a
addBoth a b =
    a + b

The Elm compiler rejects this:

-- TYPE MISMATCH

This `+` operator only works with `Int` and `Float` values.
But the type annotation says `a` which means ANY type.

After (fixed)

addBoth : number -> number -> number
addBoth a b =
    a + b

This applies to all three typeclass constraints:

Operator Before After
Elm.Op.plus a b a -> a -> a number -> number -> number
Elm.Op.lt a b a -> a -> Bool comparable -> comparable -> Bool
Elm.Op.append a b a -> a -> a appendable -> appendable -> appendable

When one operand is concrete, the constraint correctly narrows:

-- Elm.Op.plus a (Elm.float 1.0) generates:
addToFloat : Float -> Float -> Float

@dillonkearns dillonkearns force-pushed the fix/typeclass-constraints branch from dee3310 to 2401bdf Compare April 2, 2026 23:41
Comment thread tests/TypeChecking.elm Outdated
@miniBill
Copy link
Copy Markdown
Contributor

miniBill commented Apr 6, 2026

Would love to see a compappend test too

dillonkearns added a commit to dillonkearns/elm-codegen that referenced this pull request Apr 10, 2026
The compappend typeclass is satisfied by String and List comparable
(types that are both comparable and appendable). This test verifies
that a user-provided annotation with a `compappend` variable is
preserved in the generated type annotation.

Requested in PR mdgriffith#133 comment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dillonkearns dillonkearns force-pushed the fix/typeclass-constraints branch from b2236db to 2401bdf Compare April 10, 2026 18:34
When a type variable stays polymorphic through an operator that
requires a typeclass constraint (+ requires number, < requires
comparable, ++ requires appendable), the constraint was lost during
type variable rewriting, producing `a -> a -> a` instead of
`number -> number -> number`.

Root cause: `resolveVariables` replaces constrained names (like
`number_0` or `comparable`) with arg variable names (like `arg_0`).
Then `rewriteTypeVariables` renames `arg_0` to `a`, losing the
constraint.

Fix: `rewriteTypeVariablesPreservingConstraints` builds a mapping
from resolved variable names back to their constraint names by
walking the inference cache bidirectionally. If a constrained name
maps to a generic (forward: `number_0 → arg_0`) or a generic maps
to a constrained name (reverse: `arg_0 → comparable`), the
constraint name is preserved during rewriting.

Before:
  Elm.Op.plus a b  →  addBoth : a -> a -> a
  Elm.Op.lt a b    →  compareBoth : a -> a -> Bool
  Elm.Op.append a b → appendBoth : a -> a -> a

After:
  Elm.Op.plus a b  →  addBoth : number -> number -> number
  Elm.Op.lt a b    →  compareBoth : comparable -> comparable -> Bool
  Elm.Op.append a b → appendBoth : appendable -> appendable -> appendable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dillonkearns dillonkearns force-pushed the fix/typeclass-constraints branch from 2401bdf to 3022b25 Compare April 10, 2026 18:35
@dillonkearns
Copy link
Copy Markdown
Contributor Author

Would love to see a compappend test too

I tried adding a test, but since there isn't a way to use elm-codegen to generate a value of this type, it ends up not being a meaningful test case to just hardcode in the compappend type because it treats it just like a type variable. I agree it would be nice, but I think we would need an API that allows you to derive values of that type.

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.

2 participants