From 9dedc48502f83c2e20d966e4f9714a1eb9cc405a Mon Sep 17 00:00:00 2001 From: Charlon Date: Fri, 7 Nov 2025 14:08:03 +0700 Subject: [PATCH 1/2] 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. --- extra/Lamdera/Wire3/Encoder.hs | 33 ++++++++++++++- extra/Lamdera/Wire3/Helpers.hs | 76 +++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/extra/Lamdera/Wire3/Encoder.hs b/extra/Lamdera/Wire3/Encoder.hs index 8786f4bc..a9bcd15f 100644 --- a/extra/Lamdera/Wire3/Encoder.hs +++ b/extra/Lamdera/Wire3/Encoder.hs @@ -225,7 +225,38 @@ encoderForType depth ifaces cname tipe = decoder = if cname == moduleName -- Referenced type is defined in the current module - then (a (VarTopLevel moduleName generatedName)) + then + -- Check if the generated name exists, if not provide helpful error + case foreignTypeSig moduleName generatedName ifaces of + Just _ -> (a (VarTopLevel moduleName generatedName)) + Nothing -> + let + typeNameStr = Data.Name.toChars typeName + customEncoderName = Data.Name.fromChars $ "encode" ++ typeNameStr + in + case foreignTypeSig moduleName customEncoderName ifaces of + Just _ -> + error $ unlines + [ "" + , "-- WIRE3 ENCODER NOT SUPPORTED -----------------------------------------" + , "" + , "I found a custom Wire3 encoder for an opaque type:" + , "" + , " " ++ typeNameStr + , "" + , "This type cannot be used in BackendModel or FrontendModel without" + , "compiler support." + , "" + , "To fix this:" + , "" + , "1. Remove this type from your BackendModel/FrontendModel, OR" + , "" + , "2. Expose the type constructor in the module (less efficient), OR" + , "" + , "3. Add compiler support (contact Lamdera team)" + , "" + ] + Nothing -> (a (VarTopLevel moduleName generatedName)) else (a (VarForeign moduleName generatedName (getForeignSig tipe moduleName generatedName ifaces))) in diff --git a/extra/Lamdera/Wire3/Helpers.hs b/extra/Lamdera/Wire3/Helpers.hs index 471143aa..1cdbdef1 100644 --- a/extra/Lamdera/Wire3/Helpers.hs +++ b/extra/Lamdera/Wire3/Helpers.hs @@ -66,19 +66,75 @@ getForeignSig tipe moduleName generatedName ifaces = -- So add type-sig for failure encoder or decoder as appropriate. if T.isPrefixOf "w3_encode_" (T.pack $ Data.Name.toChars generatedName) then - (Forall - (Map.fromList [("a", ())]) - (TLambda (TVar "a") tLamdera_Wire_Encoder)) + let + genNameStr = Data.Name.toChars generatedName + typeNameStr = drop 10 genNameStr -- Remove "w3_encode_" prefix (10 chars) + customEncoderName = Data.Name.fromChars $ "encode" ++ typeNameStr + in + case foreignTypeSig moduleName customEncoderName ifaces of + Just _ -> + error $ unlines + [ "" + , "-- WIRE3 ENCODER NOT SUPPORTED -----------------------------------------" + , "" + , "I found a custom Wire3 encoder for an opaque type:" + , "" + , " " ++ typeNameStr + , "" + , "This type cannot be used in BackendModel or FrontendModel without" + , "compiler support." + , "" + , "To fix this:" + , "" + , "1. Remove this type from your BackendModel/FrontendModel, OR" + , "" + , "2. Expose the type constructor in the module (less efficient), OR" + , "" + , "3. Add compiler support (contact Lamdera team)" + , "" + ] + Nothing -> + (Forall + (Map.fromList [("a", ())]) + (TLambda (TVar "a") tLamdera_Wire_Encoder)) else if T.isPrefixOf "w3_decode_" (T.pack $ Data.Name.toChars generatedName) then - (Forall - (Map.fromList [("a", ())]) - (TAlias - mLamdera_Wire - "Decoder" - [("a", TVar "a")] - (Filled (TType (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") "Decoder" [TVar "a"])))) + let + genNameStr = Data.Name.toChars generatedName + typeNameStr = drop 10 genNameStr -- Remove "w3_decode_" prefix (10 chars) + customDecoderName = Data.Name.fromChars $ "decode" ++ typeNameStr + in + case foreignTypeSig moduleName customDecoderName ifaces of + Just _ -> + error $ unlines + [ "" + , "-- WIRE3 DECODER NOT SUPPORTED -----------------------------------------" + , "" + , "I found a custom Wire3 decoder for an opaque type:" + , "" + , " " ++ typeNameStr + , "" + , "This type cannot be used in BackendModel or FrontendModel without" + , "compiler support." + , "" + , "To fix this:" + , "" + , "1. Remove this type from your BackendModel/FrontendModel, OR" + , "" + , "2. Expose the type constructor in the module (less efficient), OR" + , "" + , "3. Add compiler support (contact Lamdera team)" + , "" + ] + Nothing -> + (Forall + (Map.fromList [("a", ())]) + (TAlias + mLamdera_Wire + "Decoder" + [("a", TVar "a")] + (Filled (TType (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") "Decoder" [TVar "a"])))) else error $ "impossible getForeignSig on non-wire function: " ++ Data.Name.toChars generatedName From b01dfdf713aeee4e60d64f8086700919f8bebd9b Mon Sep 17 00:00:00 2001 From: Charlon Date: Sat, 8 Nov 2025 09:55:43 +0700 Subject: [PATCH 2/2] Exclude elm/* and lamdera/* packages from custom encoder error Only show the custom encoder/decoder error for user packages, not core elm/* or lamdera/* packages like Bytes which have special handling. --- extra/Lamdera/Wire3/Encoder.hs | 10 +++++++--- extra/Lamdera/Wire3/Helpers.hs | 20 ++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/extra/Lamdera/Wire3/Encoder.hs b/extra/Lamdera/Wire3/Encoder.hs index a9bcd15f..765b9a17 100644 --- a/extra/Lamdera/Wire3/Encoder.hs +++ b/extra/Lamdera/Wire3/Encoder.hs @@ -233,9 +233,13 @@ encoderForType depth ifaces cname tipe = let typeNameStr = Data.Name.toChars typeName customEncoderName = Data.Name.fromChars $ "encode" ++ typeNameStr + + -- Only show error for user packages, not elm/* core packages + isUserPackage = case moduleName of + Module.Canonical (Name author _) _ -> author /= "elm" && author /= "lamdera" in - case foreignTypeSig moduleName customEncoderName ifaces of - Just _ -> + case (foreignTypeSig moduleName customEncoderName ifaces, isUserPackage) of + (Just _, True) -> error $ unlines [ "" , "-- WIRE3 ENCODER NOT SUPPORTED -----------------------------------------" @@ -256,7 +260,7 @@ encoderForType depth ifaces cname tipe = , "3. Add compiler support (contact Lamdera team)" , "" ] - Nothing -> (a (VarTopLevel moduleName generatedName)) + _ -> (a (VarTopLevel moduleName generatedName)) else (a (VarForeign moduleName generatedName (getForeignSig tipe moduleName generatedName ifaces))) in diff --git a/extra/Lamdera/Wire3/Helpers.hs b/extra/Lamdera/Wire3/Helpers.hs index 1cdbdef1..cfb7af40 100644 --- a/extra/Lamdera/Wire3/Helpers.hs +++ b/extra/Lamdera/Wire3/Helpers.hs @@ -70,9 +70,13 @@ getForeignSig tipe moduleName generatedName ifaces = genNameStr = Data.Name.toChars generatedName typeNameStr = drop 10 genNameStr -- Remove "w3_encode_" prefix (10 chars) customEncoderName = Data.Name.fromChars $ "encode" ++ typeNameStr + + -- Only show error for user packages, not elm/* core packages + isUserPackage = case moduleName of + Module.Canonical (Name author _) _ -> author /= "elm" && author /= "lamdera" in - case foreignTypeSig moduleName customEncoderName ifaces of - Just _ -> + case (foreignTypeSig moduleName customEncoderName ifaces, isUserPackage) of + (Just _, True) -> error $ unlines [ "" , "-- WIRE3 ENCODER NOT SUPPORTED -----------------------------------------" @@ -93,7 +97,7 @@ getForeignSig tipe moduleName generatedName ifaces = , "3. Add compiler support (contact Lamdera team)" , "" ] - Nothing -> + _ -> (Forall (Map.fromList [("a", ())]) (TLambda (TVar "a") tLamdera_Wire_Encoder)) @@ -104,9 +108,13 @@ getForeignSig tipe moduleName generatedName ifaces = genNameStr = Data.Name.toChars generatedName typeNameStr = drop 10 genNameStr -- Remove "w3_decode_" prefix (10 chars) customDecoderName = Data.Name.fromChars $ "decode" ++ typeNameStr + + -- Only show error for user packages, not elm/* core packages + isUserPackage = case moduleName of + Module.Canonical (Name author _) _ -> author /= "elm" && author /= "lamdera" in - case foreignTypeSig moduleName customDecoderName ifaces of - Just _ -> + case (foreignTypeSig moduleName customDecoderName ifaces, isUserPackage) of + (Just _, True) -> error $ unlines [ "" , "-- WIRE3 DECODER NOT SUPPORTED -----------------------------------------" @@ -127,7 +135,7 @@ getForeignSig tipe moduleName generatedName ifaces = , "3. Add compiler support (contact Lamdera team)" , "" ] - Nothing -> + _ -> (Forall (Map.fromList [("a", ())]) (TAlias