Skip to content

Commit bdca716

Browse files
committed
expand and rework docs on dicts a bit
1 parent 649dcba commit bdca716

File tree

2 files changed

+139
-86
lines changed

2 files changed

+139
-86
lines changed

pages/docs/manual/v12.0.0/dict.mdx

Lines changed: 82 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,161 @@
11
---
22
title: "Dictionary"
3-
description: "Dictionary type from ReScript Core"
3+
description: "Dictionary data structure in ReScript"
44
canonical: "/docs/manual/v12.0.0/dict"
55
---
66

77
# Dictionary
88

9-
A mutable dictionary with string keys.
10-
Compiles to a regular JavaScript object.
11-
Defined in the [Core](/docs/manual/v12.0.0/api/core/dict).
9+
ReScript has first class support for dictionaries. Dictionaries are mutable objects with string keys, where all values must have the same type. Dicts compile to regular JavaScript objects at runtime.
1210

1311
## Create
1412

15-
We have a dedicated syntax to create a new Dictionary.
13+
You can create a new dictionary in a few different ways, depending on your use case.
1614

1715
<CodeTab labels={["ReScript", "JS Output"]}>
1816

1917
```res prelude
18+
// Using the first class dict syntax
2019
let d = dict{"A": 5, "B": 6}
20+
21+
// Programatically via the standard library
22+
let d2 = Dict.fromArray([("A", 5), ("B", 6)])
2123
```
2224

2325
```js
2426
let d = {
2527
A: 5,
2628
B: 6
2729
};
28-
```
29-
30-
</CodeTab>
3130

32-
⚠️ The keys of a dictionary are always strings and the values all have the same type.
33-
You will get a compiler error if this is not the case!
31+
let d2 = Object.fromEntries([
32+
[
33+
"A",
34+
5
35+
],
36+
[
37+
"B",
38+
6
39+
]
40+
]);
3441

35-
<CodeTab labels={["ReScript", "JS Output"]}>
36-
37-
```res prelude
38-
let d = dict{"A": 5, "B": "Hej"}
3942
```
4043

41-
```js
42-
We've found a bug for you!
43-
44-
1 │ let d = dict{"A": 5, "B": "Hej"}
44+
</CodeTab>
4545

46-
This has type: string
47-
But it's expected to have type: int
48-
49-
You can convert string to int with Int.fromString.
50-
```
46+
A few things to note here:
5147

52-
</CodeTab>
48+
* Using the first class `dict{}` syntax compiles cleanly to a JavaScript object directly
49+
* Using `Dict.fromArray` is useful when you need to create a dictionary programatically
5350

5451
## Access
5552

56-
You can access values from a Dictionary either via the the Core module functions,
57-
or using pattern matching.
53+
You can access values from a Dictionary either via the the standard library `Dict` module functions, or using pattern matching.
5854

5955
<CodeTab labels={["ReScript", "JS Output"]}>
6056

6157
```res prelude
62-
let d = dict{"A": 5, "B": 6}
63-
let a : option<int> = d->Dict.get("A")
58+
let d = dict{"A": 5, "B": 6, "C": 7}
59+
60+
// Using `Dict.get`
61+
let a = d->Dict.get("A")
6462
63+
// Switching on the full dict
6564
let b = switch d {
6665
| dict{"B": b} => Some(b)
6766
| _ => None
6867
}
68+
69+
// Destructuring
70+
let dict{"C": ?c} = d
6971
```
7072

7173
```js
7274
let d = {
7375
A: 5,
74-
B: 6
76+
B: 6,
77+
C: 7
7578
};
7679

7780
let a = d["A"];
7881

7982
let b = d.B;
8083

8184
let b$1 = b !== undefined ? b : undefined;
82-
```
8385

86+
let c = d.C;
87+
```
8488
</CodeTab>
8589

86-
### Pattern match with JSON.t
90+
> In the Destructuring example, we're using the `?` optional pattern match syntax to pull out the `C` key value as an optional, regardless of if the dict has it or not.
91+
92+
## Pattern matching
93+
Dictionaries have first class support for pattern matching. Read more in the [dedicated guide on pattern matching and destructring in ReScript](pattern-matching-destructuring.md#match-on-dictionaries).
8794

88-
Pattern matching a Dictionary with the `dict{}` can be very elegant if you received an (external) [JSON.t](/docs/manual/v12.0.0/api/core/json) object.
95+
## Updating and setting values
96+
97+
You can set and update new values on your dictionary using the `Dict.set` function. All updates are mutable.
8998

9099
<CodeTab labels={["ReScript", "JS Output"]}>
91100

92101
```res prelude
93-
@module("some-module")
94-
external getSettings: string => JSON.t = "getSettings"
95-
96-
let vapidKey = switch getSettings("user") {
97-
| JSON.Object(dict{
98-
"notifications": // A nested object structure
99-
JSON.Object(dict{"vapidKey": JSON.String(key)}),
100-
}) =>
101-
Some(key)
102-
| _ => {
103-
Console.log("key not found")
104-
None
105-
}
106-
}
102+
let d = dict{"A": 5, "B": 6}
103+
104+
d->Dict.set("C", 7)
107105
```
108106

109107
```js
110-
import * as SomeModule from "some-module";
111-
112-
let match = SomeModule.getSettings("user");
113-
114-
let vapidKey;
115-
116-
if (typeof match === "object" && match !== null && !Array.isArray(match)) {
117-
let match$1 = match.notifications;
118-
if (typeof match$1 === "object" && match$1 !== null && !Array.isArray(match$1)) {
119-
let key = match$1.vapidKey;
120-
if (typeof key === "string") {
121-
vapidKey = key;
122-
} else {
123-
console.log("key not found");
124-
vapidKey = undefined;
125-
}
126-
} else {
127-
console.log("key not found");
128-
vapidKey = undefined;
129-
}
130-
} else {
131-
console.log("key not found");
132-
vapidKey = undefined;
133-
}
108+
let d = {
109+
A: 5,
110+
B: 6
111+
};
112+
113+
d["C"] = 7;
134114
```
135115

136116
</CodeTab>
137117

138-
## Mutable Update
118+
## Advanced example: Pattern matching on JSON
139119

140-
Updating an entry happens via the `Dict.set` function.
120+
JSON objects are represented as dictionaries (`dict<JSON.t>`). You can leverage that fact to decode JSON in a nice way, using only language features:
141121

142122
<CodeTab labels={["ReScript", "JS Output"]}>
143123

144124
```res prelude
145-
let d = dict{"A": 5, "B": 6}
146-
let a : option<int> = d->Dict.get("A")
125+
type user = {
126+
name: string,
127+
email: string,
128+
}
147129
148-
let b = switch d {
149-
| dict{"B": b} => Some(b)
150-
| _ => None
130+
/** Decode JSON to a `user`.
131+
let decodeUser = (json: JSON.t) => {
132+
switch json {
133+
| Object(dict{"name": JSON.String(name), "email": JSON.String(email)}) =>
134+
Some({name, email})
135+
| _ => None
136+
}
151137
}
138+
152139
```
153140

154141
```js
155-
let d = {
156-
A: 5,
157-
B: 6
158-
};
159-
160-
d["A"] = -1;
161-
162-
d["C"] = 0;
142+
function decodeUser(json) {
143+
if (typeof json !== "object" || json === null || Array.isArray(json)) {
144+
return;
145+
}
146+
let name = json.name;
147+
if (typeof name !== "string") {
148+
return;
149+
}
150+
let email = json.email;
151+
if (typeof email === "string") {
152+
return {
153+
name: name,
154+
email: email
155+
};
156+
}
157+
158+
}
163159
```
164160

165-
</CodeTab>
161+
</CodeTab>

pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,63 @@ printStudents({
602602

603603
</CodeTab>
604604

605+
### Match on Dictionaries
606+
607+
You can pattern match on dictionaries just like you can on other ReScript data structures.
608+
609+
When pattern matching on a dictionary it's assumed by default that you're expecting the keys you match on to exist in the dictionary. Example:
610+
<CodeTab labels={["ReScript", "JS Output"]}>
611+
612+
```res prelude
613+
let d = dict{"A": 5, "B": 6}
614+
615+
// We're expecting the `B` key to exist below, and `b` will be `int` in the match branch
616+
let b = switch d {
617+
| dict{"B": b} => Some(b)
618+
| _ => None
619+
}
620+
```
621+
622+
```js
623+
let d = {
624+
A: 5,
625+
B: 6
626+
};
627+
628+
let b = d.B;
629+
630+
let b$1 = b !== undefined ? b : undefined;
631+
```
632+
633+
</CodeTab>
634+
635+
However, there are situations where you want to pull out the value of a key as an option. You can do that using the `?` optional syntax in the pattern match:
636+
637+
<CodeTab labels={["ReScript", "JS Output"]}>
638+
639+
```res prelude
640+
let d = dict{"A": 5, "B": 6}
641+
642+
// We're pulling out `B` regardless of if it has a value or not, and therefore get `b` as `option<int>`
643+
let b = switch d {
644+
| dict{"B": ?b} => b
645+
}
646+
```
647+
648+
```js
649+
let d = {
650+
A: 5,
651+
B: 6
652+
};
653+
654+
let b = d.B;
655+
```
656+
657+
</CodeTab>
658+
659+
Notice how in the first case, when not using `?`, we had to supply a catch-all case `_`. That's because the pattern match _expects_ `B` to exist in the first case, for the pattern to match. If `B` doesn't exist, the match falls through to the next branch, and therefore we need to catch it to be exhaustive in our matching.
660+
661+
However, in the second case, we don't need a catch-all case. That's because the first branch will _always_ match the dictionary - either `B` exists or it doesn't, but it doesn't matter because we're pulling it out as an optional value.
605662

606663
### Small Pitfall
607664

0 commit comments

Comments
 (0)