Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire support for nested extensible records #33

Open
wants to merge 1 commit into
base: lamdera-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ expected_w3_decode_Config =

Essence from datetimepicker-legacy/src/DateTimePicker/Config.elm

@TODO remove this now?:
Tests neutered as our generation types are more specific than old Source based ones,
but the actual test was for a generation failure so this ensures the gen at least type checks

-}


Expand Down
124 changes: 124 additions & 0 deletions test/scenario-alltypes/src/Test/Wire_Record_Extensible6_Nested.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
module Test.Wire_Record_Extensible6_Nested exposing (..)

import Bytes.Decode
import Bytes.Encode
import Lamdera.Wire3


type alias Derp =
A { dog : () }



-- So working backwards from a concrete example, what would this encoder/decoder look like?


expected_w3_encode_Derp : Derp -> Lamdera.Wire3.Encoder
expected_w3_encode_Derp =
\w3_rec_var0 ->
Lamdera.Wire3.encodeSequenceWithoutLength
[ Lamdera.Wire3.encodeUnit w3_rec_var0.a
, Lamdera.Wire3.encodeUnit w3_rec_var0.dog
]


expected_w3_decode_Derp =
Lamdera.Wire3.succeedDecode
(\a0 dog0 -> { a = a0, dog = dog0 })
|> Lamdera.Wire3.andMapDecode
Lamdera.Wire3.decodeUnit
|> Lamdera.Wire3.andMapDecode
Lamdera.Wire3.decodeUnit



{-

Notice how the attributes `.a` and `.dog` are both statically called – that's because
Wire's record encoding is lexicographically ordered by field name.

We're specifically _not_ calling w3_encode_A, because if we did, we'd have to chose
between one of two implementations:

Lamdera.Wire3.encodeSequenceWithoutLength
(w3_encode_A w3_rec_var0
++ [ Lamdera.Wire3.encodeUnit w3_rec_var0.dog
]
)

Lamdera.Wire3.encodeSequenceWithoutLength
([ Lamdera.Wire3.encodeUnit w3_rec_var0.dog
]
++ w3_encode_A w3_rec_var0
)

In this specific case, the first one is correct, because `a` comes before `dog`,
but if the attribute was `x`, it would be the second one.

So to fix _that_, we'd need to return some extra info such that a caller could
sort the attributes... aaand now we're really getting into the weeds.


So we're effectively saying "if you're going to use an extensible record, then use a
type alias that concretely fills all type vars, and then use that type instead".

This leaves us with an outstanding problem for anonymous extensible records, i.e.:

type alias SomeRecord =
{ herp : String
, derp : Int
, extensible : A { dog : String }
}

Here we've got the `A { dog : String }` in the middle of the record, and we can't
just use `A { dog : String }` as a type, because it's not a concrete type, and as
we just discussed, we can't make wire encoding/decoding functions for it on the fly,
because we can't sort the attributes. It needs to be concrete.


-}
-- None of these can actually be used, because type variables must be fully applied,
-- meaning the holey extensible record itself is never directly as a value.


type alias A x =
{ x | a : () }


expected_w3_encode_A : ({ x | a : () } -> Lamdera.Wire3.Encoder) -> (A x -> Lamdera.Wire3.Encoder)
expected_w3_encode_A w3_x_c_x =
w3_x_c_x


expected_w3_decode_A : Bytes.Decode.Decoder (A { a : () })
expected_w3_decode_A =
-- "Cannot decode an extensible record with unfilled type vars"
Lamdera.Wire3.failDecode


type alias B x =
A { x | b : () }


expected_w3_encode_B : (B x -> Lamdera.Wire3.Encoder) -> (B x -> Lamdera.Wire3.Encoder)
expected_w3_encode_B w3_x_c_x =
w3_x_c_x


expected_w3_decode_B w3_x_c_x =
-- "Cannot decode an extensible record with unfilled type vars"
Lamdera.Wire3.failDecode


type alias C x =
B { x | c : () }


expected_w3_encode_C : (C x -> Lamdera.Wire3.Encoder) -> (C x -> Lamdera.Wire3.Encoder)
expected_w3_encode_C w3_x_c_x =
w3_x_c_x


expected_w3_decode_C w3_x_c_x =
-- "Cannot decode an extensible record with unfilled type vars"
Lamdera.Wire3.failDecode