Fix wire encoder+decoder for type aliases with extensible records thr…#85
Open
dillonkearns wants to merge 1 commit intolamdera:lamdera-nextfrom
Open
Conversation
…ough alias chains
When a type alias like Color = ColorValue { red, green, blue, alpha }
references an extensible record alias (ColorValue compatible =
{ compatible | value : String, color : Compatible }), the encoder and
decoder's TAlias Holey branch only checked for a direct TRecord inner
type to resolve extensible records.
When the inner type is a TAlias chain (not a direct TRecord), both
encoder and decoder fell through to normalEncoder/normalDecoder, which
passed only the extension fields to the extensible record codec —
losing the base fields (value, color). This caused a type mismatch
when the codec produced a 4-field record instead of the full 6-field
record.
Fix: in both Decoder.hs and Encoder.hs, when the Holey inner type is
not a direct TRecord, use resolveTvar to resolve through alias chains.
If this produces a Filled TRecord, inline the fully merged record
encoder/decoder.
Also set decoder Filled branch to normalDecoder to match encoder.
Test: Added Wire_Union_ForeignRecordAlias.elm test fixture with
ExternalExtensibleBase/ExternalRecordViaExtensible types in External.elm.
Verified red→green with lamdera make on a project using rtfeldman/elm-css.
Runtime roundtrip verified for Css.Color and Css.Px across 12 test cases
(DirectWrap, RecordWrap, ListWrap, Mixed unions, Complex nesting, etc).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hey @supermario! I ran into an issue, reported in elm-explorations/test#249 (comment) by @micahhahn, who hit this using
elm-snapshot(viaelm-pages run→lamdera make) with a codebase usingrtfeldman/elm-css. The salient point being thatelm-pagesscripts uselamdera makeunder the hood, and having a type definition with this particular shape caused a compiler error because of incorrect wire codegen.The Problem
When a type alias references an extensible record alias through a chain of aliases (e.g.,
Css.Color = ColorValue { red, green, blue, alpha }whereColorValue compatible = { compatible | value : String, color : Compatible }), the wire encoder and decoder only handle the extension fields (4 fields), losing the base fields from the extensible record (should be 6 fields).This causes a compiler error when any custom type wraps such a type alias:
Root cause
In both
decoderForType(Decoder.hs) andinlineIfRecordOrCall(Encoder.hs), theTAliascase'sHoleybranch checks for a directTRecordinner type to resolve extensible records viaresolvedRecordFieldMapM. But when the inner type is anotherTAliaslayer (common with packages likeelm-cssthat define aliases through multiple modules), the check doesn't fire and falls tonormalDecoder/normalEncoder. This generatesw3_decode_Color = w3_decode_ColorValue (decodeRecord {alpha, blue, green, red}), but sincew3_decode_ColorValuejust delegates to its argument, the result only handles 4 of 6 fields. Same for the encoder.Fix
In the
Holeybranch's fallthrough case for both encoder and decoder, useresolveTvarto resolve through alias chains.Reproducing In Tests
Here's what I was able to test:
test/scenario-alltypes/src/Test/Wire_Union_ForeignRecordAlias.elmwith custom types wrapping foreign extensible record aliases (mirrors theCss.Colorpattern)lamdera makeon a project usingrtfeldman/elm-css, and confirmed red→green by building a patchedlamderafrom source withstack buildCss.Color,Css.Px,Maybe Css.Color,List Css.Color, custom types with multiple variants, nested records, etc.) and confirmed that field values roundtrip correctly. That test case isn't checked in, I couldn't find a good place for that to live within the codebase, could be a good thing to explore in the future to have automated encoder/decoder roundtrip runsThe one thing I wasn't able to run locally was the full wire test suite
Test.Wire.all, maybe you could try running that in your environment as a sanity check?Let me know if you have any feedback on this, happy to make any changes or discuss!