|
| 1 | +# Lamdera Compiler Changes Needed for BiSeqDict, MultiSeqDict, MultiBiSeqDict |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +The Lamdera compiler has hardcoded support for `SeqDict` and `SeqSet` from `lamdera/containers`, but not for the new `BiSeqDict`, `MultiSeqDict`, and `MultiBiSeqDict` types. |
| 6 | + |
| 7 | +When these types are used in `BackendModel`, the compiler generates `w3_encode_*` and `w3_decode_*` wrappers with incorrect signatures. |
| 8 | + |
| 9 | +## Root Cause |
| 10 | + |
| 11 | +The codec generation is pattern-matched on specific module and type names. The compiler needs to be updated to recognize the three new types. |
| 12 | + |
| 13 | +## Files That Need Changes |
| 14 | + |
| 15 | +### 1. `extra/Lamdera/Wire3/Helpers.hs` |
| 16 | + |
| 17 | +**Add module constants** (after line 760): |
| 18 | + |
| 19 | +```haskell |
| 20 | +mLamdera_SeqDict = (Module.Canonical (Name "lamdera" "containers") "SeqDict") |
| 21 | +mLamdera_SeqSet = (Module.Canonical (Name "lamdera" "containers") "SeqSet") |
| 22 | +-- ADD THESE THREE: |
| 23 | +mLamdera_BiSeqDict = (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") |
| 24 | +mLamdera_MultiSeqDict = (Module.Canonical (Name "lamdera" "containers") "MultiSeqDict") |
| 25 | +mLamdera_MultiBiSeqDict = (Module.Canonical (Name "lamdera" "containers") "MultiBiSeqDict") |
| 26 | +``` |
| 27 | + |
| 28 | +**Add to unwrapAliasesDeep** (after line 799): |
| 29 | + |
| 30 | +```haskell |
| 31 | +TType (Module.Canonical (Name "lamdera" "containers") "SeqDict") "SeqDict" [key, val] -> |
| 32 | + TType (Module.Canonical (Name "lamdera" "containers") "SeqDict") "SeqDict" [unwrapAliasesDeep key, unwrapAliasesDeep val] |
| 33 | + |
| 34 | +-- ADD THESE THREE: |
| 35 | +TType (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") "BiSeqDict" [key, val] -> |
| 36 | + TType (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") "BiSeqDict" [unwrapAliasesDeep key, unwrapAliasesDeep val] |
| 37 | + |
| 38 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiSeqDict") "MultiSeqDict" [key, val] -> |
| 39 | + TType (Module.Canonical (Name "lamdera" "containers") "MultiSeqDict") "MultiSeqDict" [unwrapAliasesDeep key, unwrapAliasesDeep val] |
| 40 | + |
| 41 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiBiSeqDict") "MultiBiSeqDict" [key, val] -> |
| 42 | + TType (Module.Canonical (Name "lamdera" "containers") "MultiBiSeqDict") "MultiBiSeqDict" [unwrapAliasesDeep key, unwrapAliasesDeep val] |
| 43 | +``` |
| 44 | + |
| 45 | +### 2. `extra/Lamdera/Wire3/Encoder.hs` |
| 46 | + |
| 47 | +**Add to encoderForType** (after line 179): |
| 48 | + |
| 49 | +```haskell |
| 50 | +TType (Module.Canonical (Name "lamdera" "containers") "SeqDict") "SeqDict" [key, value] -> |
| 51 | + (a (VarForeign mLamdera_SeqDict "encodeDict" ...)) |
| 52 | + |
| 53 | +-- ADD THESE THREE: |
| 54 | +TType (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") "BiSeqDict" [key, value] -> |
| 55 | + (a (VarForeign mLamdera_BiSeqDict "encodeBiSeqDict" |
| 56 | + (Forall |
| 57 | + (Map.fromList [("key", ()), ("value", ())]) |
| 58 | + (TLambda |
| 59 | + (TLambda (TVar "key") tLamdera_Wire_Encoder) |
| 60 | + (TLambda (TLambda (TVar "value") tLamdera_Wire_Encoder) |
| 61 | + (TLambda |
| 62 | + (TType mLamdera_BiSeqDict "BiSeqDict" [TVar "key", TVar "value"]) |
| 63 | + tLamdera_Wire_Encoder)))))) |
| 64 | + |
| 65 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiSeqDict") "MultiSeqDict" [key, value] -> |
| 66 | + (a (VarForeign mLamdera_MultiSeqDict "encodeMultiSeqDict" |
| 67 | + (Forall |
| 68 | + (Map.fromList [("key", ()), ("value", ())]) |
| 69 | + (TLambda |
| 70 | + (TLambda (TVar "key") tLamdera_Wire_Encoder) |
| 71 | + (TLambda (TLambda (TVar "value") tLamdera_Wire_Encoder) |
| 72 | + (TLambda |
| 73 | + (TType mLamdera_MultiSeqDict "MultiSeqDict" [TVar "key", TVar "value"]) |
| 74 | + tLamdera_Wire_Encoder)))))) |
| 75 | + |
| 76 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiBiSeqDict") "MultiBiSeqDict" [key, value] -> |
| 77 | + (a (VarForeign mLamdera_MultiBiSeqDict "encodeMultiBiSeqDict" |
| 78 | + (Forall |
| 79 | + (Map.fromList [("key", ()), ("value", ())]) |
| 80 | + (TLambda |
| 81 | + (TLambda (TVar "key") tLamdera_Wire_Encoder) |
| 82 | + (TLambda (TLambda (TVar "value") tLamdera_Wire_Encoder) |
| 83 | + (TLambda |
| 84 | + (TType mLamdera_MultiBiSeqDict "MultiBiSeqDict" [TVar "key", TVar "value"]) |
| 85 | + tLamdera_Wire_Encoder)))))) |
| 86 | +``` |
| 87 | + |
| 88 | +**Add to deepEncoderForType (first occurrence)** (after line 308): |
| 89 | + |
| 90 | +```haskell |
| 91 | +TType (Module.Canonical (Name "lamdera" "containers") "SeqDict") "SeqDict" [key, val] -> |
| 92 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val ] |
| 93 | + |
| 94 | +-- ADD THESE THREE: |
| 95 | +TType (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") "BiSeqDict" [key, val] -> |
| 96 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val ] |
| 97 | + |
| 98 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiSeqDict") "MultiSeqDict" [key, val] -> |
| 99 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val ] |
| 100 | + |
| 101 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiBiSeqDict") "MultiBiSeqDict" [key, val] -> |
| 102 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val ] |
| 103 | +``` |
| 104 | + |
| 105 | +**Add to deepEncoderForType (second occurrence with value)** (after line 403): |
| 106 | + |
| 107 | +```haskell |
| 108 | +TType (Module.Canonical (Name "lamdera" "containers") "SeqDict") "SeqDict" [key, val] -> |
| 109 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val, value ] |
| 110 | + |
| 111 | +-- ADD THESE THREE: |
| 112 | +TType (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") "BiSeqDict" [key, val] -> |
| 113 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val, value ] |
| 114 | + |
| 115 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiSeqDict") "MultiSeqDict" [key, val] -> |
| 116 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val, value ] |
| 117 | + |
| 118 | +TType (Module.Canonical (Name "lamdera" "containers") "MultiBiSeqDict") "MultiBiSeqDict" [key, val] -> |
| 119 | + call (encoderForType depth ifaces cname tipe) [ deepEncoderForType depth ifaces cname key, deepEncoderForType depth ifaces cname val, value ] |
| 120 | +``` |
| 121 | + |
| 122 | +### 3. `extra/Lamdera/Wire3/Decoder.hs` |
| 123 | + |
| 124 | +**Add to decoderForType** (after line ~338, similar to SeqDict pattern): |
| 125 | + |
| 126 | +```haskell |
| 127 | +TType (Module.Canonical (Name "lamdera" "containers") "BiSeqDict") "BiSeqDict" [key, val] -> |
| 128 | + (a (Call |
| 129 | + (a (VarForeign mLamdera_BiSeqDict "decodeBiSeqDict" |
| 130 | + (Forall |
| 131 | + (Map.fromList [("k", ()), ("value", ())]) |
| 132 | + (TLambda |
| 133 | + (TAlias |
| 134 | + mLamdera_Wire |
| 135 | + "Decoder" |
| 136 | + [("a", TVar "k")] |
| 137 | + (Filled |
| 138 | + (TType |
| 139 | + (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") |
| 140 | + "Decoder" |
| 141 | + [TVar "k"]))) |
| 142 | + (TLambda |
| 143 | + (TAlias |
| 144 | + mLamdera_Wire |
| 145 | + "Decoder" |
| 146 | + [("a", TVar "value")] |
| 147 | + (Filled |
| 148 | + (TType |
| 149 | + (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") |
| 150 | + "Decoder" |
| 151 | + [TVar "value"]))) |
| 152 | + (TAlias |
| 153 | + mLamdera_Wire |
| 154 | + "Decoder" |
| 155 | + [ ( "a" |
| 156 | + , TType |
| 157 | + mLamdera_BiSeqDict |
| 158 | + "BiSeqDict" |
| 159 | + [TVar "k", TVar "value"]) |
| 160 | + ] |
| 161 | + (Filled |
| 162 | + (TType |
| 163 | + (Module.Canonical (Name "elm" "bytes") "Bytes.Decode") |
| 164 | + "Decoder" |
| 165 | + [ TType |
| 166 | + mLamdera_BiSeqDict |
| 167 | + "BiSeqDict" |
| 168 | + [TVar "k", TVar "value"] |
| 169 | + ])))))))) |
| 170 | + [deepDecoderForType depth ifaces cname key, deepDecoderForType depth ifaces cname val])) |
| 171 | + |
| 172 | +-- Similar for MultiSeqDict and MultiBiSeqDict |
| 173 | +``` |
| 174 | + |
| 175 | +### 4. `extra/Lamdera/TypeHash.hs` |
| 176 | + |
| 177 | +**Add pattern matches** (after line ~347): |
| 178 | + |
| 179 | +```haskell |
| 180 | +("lamdera", "containers", "SeqDict", "SeqDict") -> |
| 181 | + case tvarResolvedParams of |
| 182 | + key:value:_ -> ... |
| 183 | + |
| 184 | +-- ADD THESE THREE: |
| 185 | +("lamdera", "containers", "BiSeqDict", "BiSeqDict") -> |
| 186 | + case tvarResolvedParams of |
| 187 | + key:value:_ -> |
| 188 | + DHash $ (BS8.pack "BiSeqDict_") <> dhash key <> (BS8.pack "_") <> dhash value |
| 189 | + _ -> |
| 190 | + DError "❗️impossible !2 param BiSeqDict type" |
| 191 | + |
| 192 | +("lamdera", "containers", "MultiSeqDict", "MultiSeqDict") -> |
| 193 | + case tvarResolvedParams of |
| 194 | + key:value:_ -> |
| 195 | + DHash $ (BS8.pack "MultiSeqDict_") <> dhash key <> (BS8.pack "_") <> dhash value |
| 196 | + _ -> |
| 197 | + DError "❗️impossible !2 param MultiSeqDict type" |
| 198 | + |
| 199 | +("lamdera", "containers", "MultiBiSeqDict", "MultiBiSeqDict") -> |
| 200 | + case tvarResolvedParams of |
| 201 | + key:value:_ -> |
| 202 | + DHash $ (BS8.pack "MultiBiSeqDict_") <> dhash key <> (BS8.pack "_") <> dhash value |
| 203 | + _ -> |
| 204 | + DError "❗️impossible !2 param MultiBiSeqDict type" |
| 205 | +``` |
| 206 | + |
| 207 | +### 5. `extra/Lamdera/Evergreen/MigrationGenerator.hs` |
| 208 | + |
| 209 | +**Add migration helpers** (after line ~1002): |
| 210 | + |
| 211 | +```haskell |
| 212 | +("lamdera", "containers", "SeqDict", "SeqDict") -> migrate2ParamCollection ... |
| 213 | + |
| 214 | +-- ADD THESE THREE: |
| 215 | +("lamdera", "containers", "BiSeqDict", "BiSeqDict") -> migrate2ParamCollection |
| 216 | + (\m_p0 -> T.concat [ "BiSeqDict.toList |> List.map (Tuple.mapFirst ", m_p0, ") |> BiSeqDict.fromList" ]) |
| 217 | + (\m_p1 -> T.concat [ "BiSeqDict.map (\\k -> ", m_p1, ")" ]) |
| 218 | + (\m_p0 m_p1 -> T.concat [ "BiSeqDict.toList |> List.map (Tuple.mapBoth (", m_p0, ") (", m_p1, ")) |> BiSeqDict.fromList" ]) |
| 219 | + |
| 220 | +("lamdera", "containers", "MultiSeqDict", "MultiSeqDict") -> migrate2ParamCollection |
| 221 | + (\m_p0 -> T.concat [ "MultiSeqDict.toList |> List.map (Tuple.mapFirst ", m_p0, ") |> MultiSeqDict.fromList" ]) |
| 222 | + (\m_p1 -> T.concat [ "MultiSeqDict.map (\\k -> ", m_p1, ")" ]) |
| 223 | + (\m_p0 m_p1 -> T.concat [ "MultiSeqDict.toList |> List.map (Tuple.mapBoth (", m_p0, ") (", m_p1, ")) |> MultiSeqDict.fromList" ]) |
| 224 | + |
| 225 | +("lamdera", "containers", "MultiBiSeqDict", "MultiBiSeqDict") -> migrate2ParamCollection |
| 226 | + (\m_p0 -> T.concat [ "MultiBiSeqDict.toList |> List.map (Tuple.mapFirst ", m_p0, ") |> MultiBiSeqDict.fromList" ]) |
| 227 | + (\m_p1 -> T.concat [ "MultiBiSeqDict.map (\\k -> ", m_p1, ")" ]) |
| 228 | + (\m_p0 m_p1 -> T.concat [ "MultiBiSeqDict.toList |> List.map (Tuple.mapBoth (", m_p0, ") (", m_p1, ")) |> MultiBiSeqDict.fromList" ]) |
| 229 | +``` |
| 230 | + |
| 231 | +## Testing |
| 232 | + |
| 233 | +After making these changes: |
| 234 | + |
| 235 | +1. Rebuild the Lamdera compiler |
| 236 | +2. Test with the qwertytrewq repo: |
| 237 | + ```bash |
| 238 | + cd /path/to/qwertytrewq |
| 239 | + ./override-dillon.sh /path/to/containers |
| 240 | + ``` |
| 241 | + |
| 242 | +The compilation should succeed with MultiBiSeqDict in BackendModel. |
| 243 | + |
| 244 | +## Notes |
| 245 | + |
| 246 | +- All three new types follow the same pattern as SeqDict (2-parameter types with key/value) |
| 247 | +- The encoder/decoder function names in the modules are: |
| 248 | + - `encodeBiSeqDict` / `decodeBiSeqDict` |
| 249 | + - `encodeMultiSeqDict` / `decodeMultiSeqDict` |
| 250 | + - `encodeMultiBiSeqDict` / `decodeMultiBiSeqDict` |
0 commit comments