Skip to content

Commit

Permalink
Update stdlib for ADTs
Browse files Browse the repository at this point in the history
Update `std.enum` and various places of the stdlib to take into account
that now `std.typeof foo == 'Enum` might mean an enum tag or an enum
variant. Adds `std.enum.is_enum_tag` and `std.enum.is_enum_variant` to
the stdlib as well.
  • Loading branch information
yannham committed Feb 19, 2024
1 parent 8ab2c56 commit 3d3dbff
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 14 deletions.
70 changes: 60 additions & 10 deletions core/stdlib/std.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -1549,16 +1549,16 @@
error
```
"%
= std.contract.from_predicate std.is_enum,
= std.contract.from_predicate is_enum_tag,

TagOrString
| doc m%%"
Accepts both enum tags and strings. Strings are automatically
converted to an enum tag.

`TagOrString` is typically used in conjunction with an enum type, to
accept both actual enum tags and tags represented as strings (e.g. coming from a
JSON serialization).
accept both actual enum tags and tags represented as strings (e.g.
coming from a JSON serialization).

**Warning**: contracts are applied in-order. The pattern described here
requires that `TagOrString` is applied before the corresponding enum
Expand All @@ -1567,7 +1567,7 @@

# Examples

``` nickel
```nickel
let Schema = {
protocol
| std.enum.TagOrString
Expand All @@ -1589,15 +1589,63 @@
```
"%%
= fun label value =>
let label =
std.contract.label.with_message
"expected either a string or an enum tag"
label
in
%typeof% value
|> match {
'String => %enum_from_str% value,
'Enum => value,
_ =>
std.contract.blame_with_message
"expected either a string or an enum tag"
label,
'Enum =>
if is_enum_variant value then
std.contract.blame label
else
value,
_ => std.contract.blame label,
},

is_enum_tag
: Dyn -> Bool
| doc m%"
Checks if a value is an enum tag. Enum variants (applied to an argument)
aren't considered enum tags.

# Examples

```nickel
std.enum.is_enum_tag 'foo
=> true
std.enum.is_enum_tag 'FooBar
=> true
std.enum.is_enum_tag "tag"
=> false
std.enum.is_enum_tag ('Foo "arg")
=> false
```
"%
= fun value => std.is_enum value && !(%enum_is_variant% value),

is_enum_variant
: Dyn -> Bool
| doc m%"
Checks if a value is an enum variant. Bare enum tags (not applied to an
argument) aren't considered enum variants.

# Examples

```nickel
std.enum.is_enum_variant ('Foo "arg")
=> true
std.enum.is_enum_variant ('Http {version = "1.1"})
=> true
std.enum.is_enum_variant 'foo
=> false
std.enum.is_enum_variant [1, 2, 3]
=> false
```
"%
= fun value => %enum_is_variant% value,
},

function = {
Expand Down Expand Up @@ -2360,7 +2408,9 @@
|| type == 'Number
|| type == 'Bool
|| type == 'String
|| type == 'Enum
# note that `type == 'Enum` isn't sufficient, as it includes enum
# variants
|| std.enum.is_enum_tag value
),

NonEmpty
Expand Down
7 changes: 3 additions & 4 deletions doc/manual/merging.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,9 +621,9 @@ error: missing definition for `required_field2`
8 │ & { foo.required_field1 = "here" }
│ ------------------------ in this record
┌─ <stdlib/std.ncl>:3012:18
┌─ <stdlib/std.ncl>:3062:18
3012 │ = fun x y => %deep_seq% x y,
3062 │ = fun x y => %deep_seq% x y,
│ ------------ accessed here
```

Expand All @@ -640,8 +640,7 @@ let Port
(
fun value =>
std.is_number value
&& value
% 1 == 0
&& value % 1 == 0
&& value >= 0
&& value <= 65535
)
Expand Down

0 comments on commit 3d3dbff

Please sign in to comment.