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

WIP: Make Encoding/Decoding generic over errors #6846

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
56 changes: 26 additions & 30 deletions crates/compiler/builtins/roc/Decode.roc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
module [
DecodeError,
DecodeResult,
Decoder,
Decoding,
Expand Down Expand Up @@ -49,9 +48,6 @@ import Num exposing [
]
import Bool exposing [Bool]

## Error types when decoding a `List U8` of utf-8 bytes using a [Decoder]
DecodeError : [TooShort]

## Return type of a [Decoder].
##
## This can be useful when creating a [custom](#custom) decoder or when
Expand All @@ -65,43 +61,43 @@ DecodeError : [TooShort]
##
## actual.result == expected
## ```
DecodeResult val : { result : Result val DecodeError, rest : List U8 }
DecodeResult val err : { result : Result val err, rest : List U8 }

## Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded
## value, and `fmt` is a [Decoder] which implements the [DecoderFormatting]
## ability
Decoder val fmt := List U8, fmt -> DecodeResult val where fmt implements DecoderFormatting
Decoder val err fmt := List U8, fmt -> DecodeResult val err where fmt implements DecoderFormatting

## Definition of the [Decoding] ability
Decoding implements
decoder : Decoder val fmt where val implements Decoding, fmt implements DecoderFormatting
decoder : Decoder val err fmt where val implements Decoding, fmt implements DecoderFormatting

## Definition of the [DecoderFormatting] ability
DecoderFormatting implements
u8 : Decoder U8 fmt where fmt implements DecoderFormatting
u16 : Decoder U16 fmt where fmt implements DecoderFormatting
u32 : Decoder U32 fmt where fmt implements DecoderFormatting
u64 : Decoder U64 fmt where fmt implements DecoderFormatting
u128 : Decoder U128 fmt where fmt implements DecoderFormatting
i8 : Decoder I8 fmt where fmt implements DecoderFormatting
i16 : Decoder I16 fmt where fmt implements DecoderFormatting
i32 : Decoder I32 fmt where fmt implements DecoderFormatting
i64 : Decoder I64 fmt where fmt implements DecoderFormatting
i128 : Decoder I128 fmt where fmt implements DecoderFormatting
f32 : Decoder F32 fmt where fmt implements DecoderFormatting
f64 : Decoder F64 fmt where fmt implements DecoderFormatting
dec : Decoder Dec fmt where fmt implements DecoderFormatting
bool : Decoder Bool fmt where fmt implements DecoderFormatting
string : Decoder Str fmt where fmt implements DecoderFormatting
list : Decoder elem fmt -> Decoder (List elem) fmt where fmt implements DecoderFormatting
u8 : Decoder U8 err fmt where fmt implements DecoderFormatting
u16 : Decoder U16 err fmt where fmt implements DecoderFormatting
u32 : Decoder U32 err fmt where fmt implements DecoderFormatting
u64 : Decoder U64 err fmt where fmt implements DecoderFormatting
u128 : Decoder U128 err fmt where fmt implements DecoderFormatting
i8 : Decoder I8 err fmt where fmt implements DecoderFormatting
i16 : Decoder I16 err fmt where fmt implements DecoderFormatting
i32 : Decoder I32 err fmt where fmt implements DecoderFormatting
i64 : Decoder I64 err fmt where fmt implements DecoderFormatting
i128 : Decoder I128 err fmt where fmt implements DecoderFormatting
f32 : Decoder F32 err fmt where fmt implements DecoderFormatting
f64 : Decoder F64 err fmt where fmt implements DecoderFormatting
dec : Decoder Dec err fmt where fmt implements DecoderFormatting
bool : Decoder Bool err fmt where fmt implements DecoderFormatting
string : Decoder Str err fmt where fmt implements DecoderFormatting
list : Decoder elem err fmt -> Decoder (List elem) err fmt where fmt implements DecoderFormatting

## `record state stepField finalizer` decodes a record field-by-field.
##
## `stepField` returns a decoder for the given field in the record, or
## `Skip` if the field is not a part of the decoded record.
##
## `finalizer` should produce the record value from the decoded `state`.
record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state, fmt -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
record : state, (state, Str -> [Keep (Decoder state err fmt), Skip]), (state, fmt -> Result val err) -> Decoder val err fmt where fmt implements DecoderFormatting

## `tuple state stepElem finalizer` decodes a tuple element-by-element.
##
Expand All @@ -110,7 +106,7 @@ DecoderFormatting implements
## index passed to `stepElem` is 0-indexed.
##
## `finalizer` should produce the tuple value from the decoded `state`.
tuple : state, (state, U64 -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
tuple : state, (state, U64 -> [Next (Decoder state err fmt), TooLong]), (state -> Result val err) -> Decoder val err fmt where fmt implements DecoderFormatting

## Build a custom [Decoder] function. For example the implementation of
## `decodeBool` could be defined as follows;
Expand All @@ -122,11 +118,11 @@ DecoderFormatting implements
## ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.dropFirst bytes 4 }
## _ -> { result: Err TooShort, rest: bytes }
## ```
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
custom : (List U8, fmt -> DecodeResult val err) -> Decoder val err fmt where fmt implements DecoderFormatting
custom = \decode -> @Decoder decode

## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
decodeWith : List U8, Decoder val err fmt, fmt -> DecodeResult val err where fmt implements DecoderFormatting
decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt

## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
Expand All @@ -138,7 +134,7 @@ decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
##
## actual.result == expected
## ```
fromBytesPartial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting
fromBytesPartial : List U8, fmt -> DecodeResult val err where val implements Decoding, fmt implements DecoderFormatting
fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt

## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes
Expand All @@ -152,7 +148,7 @@ fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
##
## actual == expected
## ```
fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting
fromBytes : List U8, fmt -> Result val [Leftover (List U8), TooShort] where val implements Decoding, fmt implements DecoderFormatting
fromBytes = \bytes, fmt ->
when fromBytesPartial bytes fmt is
{ result, rest } ->
Expand All @@ -164,5 +160,5 @@ fromBytes = \bytes, fmt ->
Err (Leftover rest)

## Transform the `val` of a [DecodeResult]
mapResult : DecodeResult a, (a -> b) -> DecodeResult b
mapResult : DecodeResult a err, (a -> b) -> DecodeResult b err
mapResult = \{ result, rest }, mapper -> { result: Result.map result mapper, rest }
2 changes: 1 addition & 1 deletion crates/compiler/derive/src/decoding/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
// set val ~ elem
let val_var = match env.subs.get_content_without_compacting(elem_decoder_var) {
Content::Alias(Symbol::DECODE_DECODER_OPAQUE, vars, _, AliasKind::Opaque)
if vars.type_variables_len == 2 =>
if vars.type_variables_len == 3 =>
{
env.subs.get_subs_slice(vars.type_variables())[0]
}
Expand Down
6 changes: 3 additions & 3 deletions crates/compiler/derive/src/decoding/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use super::wrap_in_decode_custom_decode_with;
/// Ok f1 -> Ok { f1, f0 }
/// Err _ -> Err TooShort
/// Err _ -> Err TooShort
///
///
/// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.record initialState stepField finalizer) fmt
///```
pub(crate) fn decoder(
Expand Down Expand Up @@ -888,7 +888,7 @@ pub(super) fn finalizer(
.zip(field_vars.iter().rev())
.zip(result_field_vars.iter().rev())
{
// [Ok field_var, Err DecodeError]
// [Ok field_var, Err err]
// when rec.f0 is
// Err _ ->
// when Decode.decodeWith [] Decode.decoder fmt is
Expand Down Expand Up @@ -1020,7 +1020,7 @@ pub(super) fn finalizer(
/// decRec-> decRec.result
/// Ok a -> Ok a
/// ```
/// Tries to decode the field with a zero byte input if it missing,
/// Tries to decode the field with a zero byte input if it missing,
/// this allows the decoder to decode types that have a state for "missing", such as
/// an "Option" type.
///
Expand Down
57 changes: 28 additions & 29 deletions crates/compiler/module/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1577,35 +1577,34 @@ define_builtins! {
26 ENCODE_TO_BYTES: "toBytes"
}
12 DECODE: "Decode" => {
0 DECODE_DECODE_ERROR: "DecodeError" exposed_type=true
1 DECODE_DECODE_RESULT: "DecodeResult" exposed_type=true
2 DECODE_DECODER_OPAQUE: "Decoder" exposed_type=true
3 DECODE_DECODING: "Decoding" exposed_type=true
4 DECODE_DECODER: "decoder"
5 DECODE_DECODERFORMATTING: "DecoderFormatting" exposed_type=true
6 DECODE_U8: "u8"
7 DECODE_U16: "u16"
8 DECODE_U32: "u32"
9 DECODE_U64: "u64"
10 DECODE_U128: "u128"
11 DECODE_I8: "i8"
12 DECODE_I16: "i16"
13 DECODE_I32: "i32"
14 DECODE_I64: "i64"
15 DECODE_I128: "i128"
16 DECODE_F32: "f32"
17 DECODE_F64: "f64"
18 DECODE_DEC: "dec"
19 DECODE_BOOL: "bool"
20 DECODE_STRING: "string"
21 DECODE_LIST: "list"
22 DECODE_RECORD: "record"
23 DECODE_TUPLE: "tuple"
24 DECODE_CUSTOM: "custom"
25 DECODE_DECODE_WITH: "decodeWith"
26 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial"
27 DECODE_FROM_BYTES: "fromBytes"
28 DECODE_MAP_RESULT: "mapResult"
0 DECODE_DECODE_RESULT: "DecodeResult" exposed_type=true
1 DECODE_DECODER_OPAQUE: "Decoder" exposed_type=true
2 DECODE_DECODING: "Decoding" exposed_type=true
3 DECODE_DECODER: "decoder"
4 DECODE_DECODERFORMATTING: "DecoderFormatting" exposed_type=true
5 DECODE_U8: "u8"
6 DECODE_U16: "u16"
7 DECODE_U32: "u32"
8 DECODE_U64: "u64"
9 DECODE_U128: "u128"
10 DECODE_I8: "i8"
11 DECODE_I16: "i16"
12 DECODE_I32: "i32"
13 DECODE_I64: "i64"
14 DECODE_I128: "i128"
15 DECODE_F32: "f32"
16 DECODE_F64: "f64"
17 DECODE_DEC: "dec"
18 DECODE_BOOL: "bool"
19 DECODE_STRING: "string"
20 DECODE_LIST: "list"
21 DECODE_RECORD: "record"
22 DECODE_TUPLE: "tuple"
23 DECODE_CUSTOM: "custom"
24 DECODE_DECODE_WITH: "decodeWith"
25 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial"
26 DECODE_FROM_BYTES: "fromBytes"
27 DECODE_MAP_RESULT: "mapResult"
}
13 HASH: "Hash" => {
0 HASH_HASH_ABILITY: "Hash" exposed_type=true
Expand Down
6 changes: 3 additions & 3 deletions crates/compiler/test_mono/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ decodeF64 = Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest
decodeDec = Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest }
decodeBool = Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest }
decodeString = Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest }
decodeList : Decoder elem ErrDecoder -> Decoder (List elem) ErrDecoder
decodeList : Decoder elem err ErrDecoder -> Decoder (List elem) err ErrDecoder
decodeList = \_ -> Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest }
decodeRecord : state, (state, Str -> [Keep (Decoder state ErrDecoder), Skip]), (state, ErrDecoder -> Result val DecodeError) -> Decoder val ErrDecoder
decodeRecord : state, (state, Str -> [Keep (Decoder state err ErrDecoder), Skip]), (state, ErrDecoder -> Result val err) -> Decoder val err ErrDecoder
decodeRecord = \_, _, _ -> Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest }
decodeTuple : state, (state, U64 -> [Next (Decoder state ErrDecoder), TooLong]), (state -> Result val DecodeError) -> Decoder val ErrDecoder
decodeTuple : state, (state, U64 -> [Next (Decoder state err ErrDecoder), TooLong]), (state -> Result val err) -> Decoder val err ErrDecoder
decodeTuple = \_, _, _ -> Decode.custom \rest, @ErrDecoder {} -> { result: Err TooShort, rest }
"#;

Expand Down