Skip to content

Commit a1f32fd

Browse files
committed
Improve Wire3 error message for opaque types with custom encoders/decoders
Instead of the cryptic "TOO MANY ARGS" error, provide a helpful message that explains: - The issue: opaque type with custom encoder but compiler can't generate wrapper - Three solutions: expose constructor, add compiler support, or remove from Wire3 types This helps users understand what went wrong and how to fix it when they try to use opaque types like BiSeqDict in their BackendModel/FrontendModel without compiler support.
1 parent 94a4e63 commit a1f32fd

File tree

2 files changed

+104
-11
lines changed

2 files changed

+104
-11
lines changed

extra/Lamdera/Wire3/Encoder.hs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,40 @@ encoderForType depth ifaces cname tipe =
225225
decoder =
226226
if cname == moduleName
227227
-- Referenced type is defined in the current module
228-
then (a (VarTopLevel moduleName generatedName))
228+
then
229+
-- Check if the generated name exists, if not provide helpful error
230+
case foreignTypeSig moduleName generatedName ifaces of
231+
Just _ -> (a (VarTopLevel moduleName generatedName))
232+
Nothing ->
233+
let
234+
typeNameStr = Data.Name.toChars typeName
235+
customEncoderName = Data.Name.fromChars $ "encode" ++ typeNameStr
236+
in
237+
case foreignTypeSig moduleName customEncoderName ifaces of
238+
Just _ ->
239+
error $ unlines
240+
[ ""
241+
, "-- WIRE3 ENCODER ISSUE -------------------------------------------------"
242+
, ""
243+
, "I found an opaque type with a custom Wire3 encoder:"
244+
, ""
245+
, " " ++ typeNameStr
246+
, ""
247+
, "The encoder `encode" ++ typeNameStr ++ "` exists, but I can't generate the"
248+
, "correct wrapper automatically because the type constructor is not exposed."
249+
, ""
250+
, "You have 3 options:"
251+
, ""
252+
, "1. Expose the type constructor (simple but wasteful - sends internal structure)"
253+
, " "
254+
, "2. Add compiler support (efficient - only sends what's needed)"
255+
, " Make a PR to lamdera/compiler following the SeqDict pattern"
256+
, " See: https://github.com/lamdera/compiler/blob/master/extra/Lamdera/Wire3/Encoder.hs#L170"
257+
, " "
258+
, "3. Remove from BackendModel/FrontendModel (if not needed for Wire3)"
259+
, ""
260+
]
261+
Nothing -> (a (VarTopLevel moduleName generatedName))
229262
else (a (VarForeign moduleName generatedName (getForeignSig tipe moduleName generatedName ifaces)))
230263

231264
in

extra/Lamdera/Wire3/Helpers.hs

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,79 @@ getForeignSig tipe moduleName generatedName ifaces =
6666
-- So add type-sig for failure encoder or decoder as appropriate.
6767
if T.isPrefixOf "w3_encode_" (T.pack $ Data.Name.toChars generatedName)
6868
then
69-
(Forall
70-
(Map.fromList [("a", ())])
71-
(TLambda (TVar "a") tLamdera_Wire_Encoder))
69+
let
70+
genNameStr = Data.Name.toChars generatedName
71+
typeNameStr = drop 10 genNameStr -- Remove "w3_encode_" prefix (10 chars)
72+
customEncoderName = Data.Name.fromChars $ "encode" ++ typeNameStr
73+
in
74+
case foreignTypeSig moduleName customEncoderName ifaces of
75+
Just _ ->
76+
error $ unlines
77+
[ ""
78+
, "-- WIRE3 ENCODER ISSUE -------------------------------------------------"
79+
, ""
80+
, "I found an opaque type with a custom Wire3 encoder:"
81+
, ""
82+
, " " ++ typeNameStr
83+
, ""
84+
, "The encoder `encode" ++ typeNameStr ++ "` exists, but I can't generate the"
85+
, "correct wrapper automatically because the type constructor is not exposed."
86+
, ""
87+
, "You have 3 options:"
88+
, ""
89+
, "1. Expose the type constructor (simple but wasteful - sends internal structure)"
90+
, " "
91+
, "2. Add compiler support (efficient - only sends what's needed)"
92+
, " Make a PR to lamdera/compiler following the SeqDict pattern"
93+
, " See: https://github.com/lamdera/compiler/blob/master/extra/Lamdera/Wire3/Encoder.hs#L170"
94+
, " "
95+
, "3. Remove from BackendModel/FrontendModel (if not needed for Wire3)"
96+
, ""
97+
]
98+
Nothing ->
99+
(Forall
100+
(Map.fromList [("a", ())])
101+
(TLambda (TVar "a") tLamdera_Wire_Encoder))
72102

73103
else if T.isPrefixOf "w3_decode_" (T.pack $ Data.Name.toChars generatedName)
74104
then
75-
(Forall
76-
(Map.fromList [("a", ())])
77-
(TAlias
78-
mLamdera_Wire
79-
"Decoder"
80-
[("a", TVar "a")]
81-
(Filled (TType (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") "Decoder" [TVar "a"]))))
105+
let
106+
genNameStr = Data.Name.toChars generatedName
107+
typeNameStr = drop 10 genNameStr -- Remove "w3_decode_" prefix (10 chars)
108+
customDecoderName = Data.Name.fromChars $ "decode" ++ typeNameStr
109+
in
110+
case foreignTypeSig moduleName customDecoderName ifaces of
111+
Just _ ->
112+
error $ unlines
113+
[ ""
114+
, "-- WIRE3 DECODER ISSUE -------------------------------------------------"
115+
, ""
116+
, "I found an opaque type with a custom Wire3 decoder:"
117+
, ""
118+
, " " ++ typeNameStr
119+
, ""
120+
, "The decoder `decode" ++ typeNameStr ++ "` exists, but I can't generate the"
121+
, "correct wrapper automatically because the type constructor is not exposed."
122+
, ""
123+
, "You have 3 options:"
124+
, ""
125+
, "1. Expose the type constructor (simple but wasteful - sends internal structure)"
126+
, " "
127+
, "2. Add compiler support (efficient - only sends what's needed)"
128+
, " Make a PR to lamdera/compiler following the SeqDict pattern"
129+
, " See: https://github.com/lamdera/compiler/blob/master/extra/Lamdera/Wire3/Decoder.hs#L305"
130+
, " "
131+
, "3. Remove from BackendModel/FrontendModel (if not needed for Wire3)"
132+
, ""
133+
]
134+
Nothing ->
135+
(Forall
136+
(Map.fromList [("a", ())])
137+
(TAlias
138+
mLamdera_Wire
139+
"Decoder"
140+
[("a", TVar "a")]
141+
(Filled (TType (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") "Decoder" [TVar "a"]))))
82142
else
83143
error $ "impossible getForeignSig on non-wire function: " ++ Data.Name.toChars generatedName
84144

0 commit comments

Comments
 (0)