Skip to content

Commit ac320b8

Browse files
committed
Update test repo with working opaque ID types and PR references
- Add Id.elm with proper opaque ID types (ChatId, DocumentId) - Update Backend.elm to use Id types instead of Never constructors - Update Types.elm to use Id ChatId and Id DocumentId - Update README with links to both PRs and test results - Add COMPILER_CHANGES_NEEDED.md documentation - Install UUID package dependency Test now compiles successfully with modified compiler! Related PRs: - lamdera/containers#1 - lamdera/compiler#69
1 parent 591acba commit ac320b8

File tree

6 files changed

+400
-100
lines changed

6 files changed

+400
-100
lines changed

COMPILER_CHANGES_NEEDED.md

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
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`

README.md

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,78 @@
1-
# LOVR Override Test for lamdera/containers
1+
# Test Repository for lamdera/containers New Types
22

3-
**Successfully testing new modules (BiSeqDict, MultiSeqDict, MultiBiSeqDict) added to `lamdera/containers` using the LOVR override mechanism!**
3+
**Successfully testing BiSeqDict, MultiSeqDict, and MultiBiSeqDict with full compiler support!**
44

5-
## Setup
5+
This repository demonstrates the three new container types added to `lamdera/containers` and verifies that the Lamdera compiler correctly generates Wire3 codecs for them.
66

7-
Use Dillon's improved script (requires jq):
7+
## Related PRs
88

9-
```bash
10-
./override-dillon.sh /path/to/containers
11-
```
9+
- **Container types**: [lamdera/containers#1](https://github.com/lamdera/containers/pull/1)
10+
- **Compiler support**: [lamdera/compiler#69](https://github.com/lamdera/compiler/pull/69)
1211

13-
This will automatically:
14-
1. Copy the package files
15-
2. Build the override
16-
3. Generate pack.zip and endpoint.json
17-
4. Clean caches
18-
5. Start lamdera live with the override
12+
Both PRs are required for the new types to work in Lamdera apps.
1913

20-
**Bug Fix:** The original script had a bug in endpoint.json URL generation - it was missing the author part of the package name. Fixed in this repo's version.
21-
22-
## Manual Testing
14+
## Setup
2315

24-
To compile without starting lamdera live:
16+
Use the improved override script (requires jq):
2517

2618
```bash
27-
LDEBUG=1 EXPERIMENTAL=1 LOVR=/path/to/qwertytrewq/overrides lamdera make src/Backend.elm
19+
./override-dillon.sh /path/to/containers
2820
```
2921

30-
## Result
22+
This automatically:
23+
1. Copies package files
24+
2. Builds the override
25+
3. Generates pack.zip and endpoint.json
26+
4. Cleans caches
27+
5. Starts lamdera live with the override
3128

32-
✅ BiSeqDict, MultiSeqDict, and MultiBiSeqDict are now importable!
29+
**Bug Fix**: Fixed endpoint.json URL generation bug (was missing author name).
3330

34-
⚠️ **Codec Generation Issue**: There's a Lamdera compiler issue with automatic codec wrapper generation for these new types.
31+
## Testing with Modified Compiler
3532

36-
### The Problem
33+
To test with the compiler changes from [lamdera/compiler#69](https://github.com/lamdera/compiler/pull/69):
3734

38-
When using these types in `BackendModel` (see `src/Types.elm`):
35+
```bash
36+
# Build modified compiler
37+
cd /path/to/lamdera-compiler
38+
stack install
3939

40-
```elm
41-
type alias BackendModel =
42-
{ message : String
43-
, chatDocuments : MultiBiSeqDict ChatId DocumentId
44-
, documents : List Document
45-
}
40+
# Test compilation
41+
cd /path/to/qwertytrewq
42+
LDEBUG=1 EXPERIMENTAL=1 LOVR="$(pwd)/overrides" lamdera make src/Backend.elm
4643
```
4744

48-
The compiler generates `w3_encode_*` and `w3_decode_*` wrappers with incorrect signatures:
45+
## Results
4946

47+
**All three types work perfectly with proper compiler support!**
48+
49+
### Before Compiler Fix
5050
```
5151
-- TOO MANY ARGS ------------------------------------------------- src/Types.elm
5252
5353
The `w3_decode_MultiBiSeqDict` value is not a function, but it was given 2 arguments.
5454
The `w3_encode_MultiBiSeqDict` function expects 1 argument, but it got 3 instead.
5555
```
5656

57-
### What's Implemented
58-
59-
The encoder/decoder functions in the modules ARE implemented correctly (following SeqDict/SeqSet pattern):
60-
61-
- `encodeMultiBiSeqDict : (key -> Encoder) -> (value -> Encoder) -> MultiBiSeqDict key value -> Encoder`
62-
- `decodeMultiBiSeqDict : Decoder k -> Decoder value -> Decoder (MultiBiSeqDict k value)`
63-
64-
But the compiler's automatic wrapper generation produces incorrect signatures.
57+
### After Compiler Fix
58+
```
59+
Success! Compiled 4 modules.
60+
```
6561

66-
### Demo Code
62+
## Demo Code
6763

68-
`src/Backend.elm` contains a complete working demo with:
69-
- Opaque ID types (`ChatId`, `DocumentId`)
70-
- Fake S3 document generation
64+
`src/Backend.elm` demonstrates real-world usage:
65+
- Opaque ID types using the `Id a` pattern (ChatId, DocumentId)
66+
- Many-to-many relationship: chats ↔ documents
7167
- Helper functions: `getDocumentsInChat`, `getChatsWithDocument`, `transferDocument`
72-
- Example many-to-many chat ↔ documents relationship
68+
- Example operations showing bidirectional queries
69+
70+
```elm
71+
type alias BackendModel =
72+
{ message : String
73+
, chatDocuments : MultiBiSeqDict (Id ChatId) (Id DocumentId)
74+
, documents : List Document
75+
}
76+
```
7377

74-
Related PR: https://github.com/lamdera/containers/pull/1
78+
The implementation shows how these types work with opaque types (not limited to `comparable`) and demonstrates efficient bidirectional lookups for many-to-many relationships.

0 commit comments

Comments
 (0)