From 649dcbab694459b5b16448538ce39088458792ce Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 2 May 2025 16:49:50 +0200 Subject: [PATCH 1/3] Initial dictionary page --- data/sidebar_manual_v1200.json | 1 + pages/docs/manual/v12.0.0/dict.mdx | 165 +++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 pages/docs/manual/v12.0.0/dict.mdx diff --git a/data/sidebar_manual_v1200.json b/data/sidebar_manual_v1200.json index a68a5336c..781493d78 100644 --- a/data/sidebar_manual_v1200.json +++ b/data/sidebar_manual_v1200.json @@ -14,6 +14,7 @@ "tuple", "record", "object", + "dict", "variant", "polymorphic-variant", "null-undefined-option", diff --git a/pages/docs/manual/v12.0.0/dict.mdx b/pages/docs/manual/v12.0.0/dict.mdx new file mode 100644 index 000000000..44f799e48 --- /dev/null +++ b/pages/docs/manual/v12.0.0/dict.mdx @@ -0,0 +1,165 @@ +--- +title: "Dictionary" +description: "Dictionary type from ReScript Core" +canonical: "/docs/manual/v12.0.0/dict" +--- + +# Dictionary + +A mutable dictionary with string keys. +Compiles to a regular JavaScript object. +Defined in the [Core](/docs/manual/v12.0.0/api/core/dict). + +## Create + +We have a dedicated syntax to create a new Dictionary. + + + +```res prelude +let d = dict{"A": 5, "B": 6} +``` + +```js +let d = { + A: 5, + B: 6 +}; +``` + + + +⚠️ The keys of a dictionary are always strings and the values all have the same type. +You will get a compiler error if this is not the case! + + + +```res prelude +let d = dict{"A": 5, "B": "Hej"} +``` + +```js +We've found a bug for you! + + 1 │ let d = dict{"A": 5, "B": "Hej"} + + This has type: string + But it's expected to have type: int + + You can convert string to int with Int.fromString. +``` + + + +## Access + +You can access values from a Dictionary either via the the Core module functions, +or using pattern matching. + + + +```res prelude +let d = dict{"A": 5, "B": 6} +let a : option = d->Dict.get("A") + +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} +``` + +```js +let d = { + A: 5, + B: 6 +}; + +let a = d["A"]; + +let b = d.B; + +let b$1 = b !== undefined ? b : undefined; +``` + + + +### Pattern match with JSON.t + +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. + + + +```res prelude +@module("some-module") +external getSettings: string => JSON.t = "getSettings" + +let vapidKey = switch getSettings("user") { +| JSON.Object(dict{ + "notifications": // A nested object structure + JSON.Object(dict{"vapidKey": JSON.String(key)}), + }) => + Some(key) +| _ => { + Console.log("key not found") + None + } +} +``` + +```js +import * as SomeModule from "some-module"; + +let match = SomeModule.getSettings("user"); + +let vapidKey; + +if (typeof match === "object" && match !== null && !Array.isArray(match)) { + let match$1 = match.notifications; + if (typeof match$1 === "object" && match$1 !== null && !Array.isArray(match$1)) { + let key = match$1.vapidKey; + if (typeof key === "string") { + vapidKey = key; + } else { + console.log("key not found"); + vapidKey = undefined; + } + } else { + console.log("key not found"); + vapidKey = undefined; + } +} else { + console.log("key not found"); + vapidKey = undefined; +} +``` + + + +## Mutable Update + +Updating an entry happens via the `Dict.set` function. + + + +```res prelude +let d = dict{"A": 5, "B": 6} +let a : option = d->Dict.get("A") + +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} +``` + +```js +let d = { + A: 5, + B: 6 +}; + +d["A"] = -1; + +d["C"] = 0; +``` + + \ No newline at end of file From bdca716b6f4bbfadb41071ccec58cf2a1495fa1b Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 5 May 2025 10:17:46 +0200 Subject: [PATCH 2/3] expand and rework docs on dicts a bit --- pages/docs/manual/v12.0.0/dict.mdx | 168 +++++++++--------- .../pattern-matching-destructuring.mdx | 57 ++++++ 2 files changed, 139 insertions(+), 86 deletions(-) diff --git a/pages/docs/manual/v12.0.0/dict.mdx b/pages/docs/manual/v12.0.0/dict.mdx index 44f799e48..00b3f2b41 100644 --- a/pages/docs/manual/v12.0.0/dict.mdx +++ b/pages/docs/manual/v12.0.0/dict.mdx @@ -1,23 +1,25 @@ --- title: "Dictionary" -description: "Dictionary type from ReScript Core" +description: "Dictionary data structure in ReScript" canonical: "/docs/manual/v12.0.0/dict" --- # Dictionary -A mutable dictionary with string keys. -Compiles to a regular JavaScript object. -Defined in the [Core](/docs/manual/v12.0.0/api/core/dict). +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. ## Create -We have a dedicated syntax to create a new Dictionary. +You can create a new dictionary in a few different ways, depending on your use case. ```res prelude +// Using the first class dict syntax let d = dict{"A": 5, "B": 6} + +// Programatically via the standard library +let d2 = Dict.fromArray([("A", 5), ("B", 6)]) ``` ```js @@ -25,53 +27,54 @@ let d = { A: 5, B: 6 }; -``` - - -⚠️ The keys of a dictionary are always strings and the values all have the same type. -You will get a compiler error if this is not the case! +let d2 = Object.fromEntries([ + [ + "A", + 5 + ], + [ + "B", + 6 + ] +]); - - -```res prelude -let d = dict{"A": 5, "B": "Hej"} ``` -```js -We've found a bug for you! - - 1 │ let d = dict{"A": 5, "B": "Hej"} + - This has type: string - But it's expected to have type: int - - You can convert string to int with Int.fromString. -``` +A few things to note here: - +* Using the first class `dict{}` syntax compiles cleanly to a JavaScript object directly +* Using `Dict.fromArray` is useful when you need to create a dictionary programatically ## Access -You can access values from a Dictionary either via the the Core module functions, -or using pattern matching. +You can access values from a Dictionary either via the the standard library `Dict` module functions, or using pattern matching. ```res prelude -let d = dict{"A": 5, "B": 6} -let a : option = d->Dict.get("A") +let d = dict{"A": 5, "B": 6, "C": 7} + +// Using `Dict.get` +let a = d->Dict.get("A") +// Switching on the full dict let b = switch d { | dict{"B": b} => Some(b) | _ => None } + +// Destructuring +let dict{"C": ?c} = d ``` ```js let d = { A: 5, - B: 6 + B: 6, + C: 7 }; let a = d["A"]; @@ -79,87 +82,80 @@ let a = d["A"]; let b = d.B; let b$1 = b !== undefined ? b : undefined; -``` +let c = d.C; +``` -### Pattern match with JSON.t +> 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. + +## Pattern matching +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). -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. +## Updating and setting values + +You can set and update new values on your dictionary using the `Dict.set` function. All updates are mutable. ```res prelude -@module("some-module") -external getSettings: string => JSON.t = "getSettings" - -let vapidKey = switch getSettings("user") { -| JSON.Object(dict{ - "notifications": // A nested object structure - JSON.Object(dict{"vapidKey": JSON.String(key)}), - }) => - Some(key) -| _ => { - Console.log("key not found") - None - } -} +let d = dict{"A": 5, "B": 6} + +d->Dict.set("C", 7) ``` ```js -import * as SomeModule from "some-module"; - -let match = SomeModule.getSettings("user"); - -let vapidKey; - -if (typeof match === "object" && match !== null && !Array.isArray(match)) { - let match$1 = match.notifications; - if (typeof match$1 === "object" && match$1 !== null && !Array.isArray(match$1)) { - let key = match$1.vapidKey; - if (typeof key === "string") { - vapidKey = key; - } else { - console.log("key not found"); - vapidKey = undefined; - } - } else { - console.log("key not found"); - vapidKey = undefined; - } -} else { - console.log("key not found"); - vapidKey = undefined; -} +let d = { + A: 5, + B: 6 +}; + +d["C"] = 7; ``` -## Mutable Update +## Advanced example: Pattern matching on JSON -Updating an entry happens via the `Dict.set` function. +JSON objects are represented as dictionaries (`dict`). You can leverage that fact to decode JSON in a nice way, using only language features: ```res prelude -let d = dict{"A": 5, "B": 6} -let a : option = d->Dict.get("A") +type user = { + name: string, + email: string, +} -let b = switch d { -| dict{"B": b} => Some(b) -| _ => None +/** Decode JSON to a `user`. +let decodeUser = (json: JSON.t) => { + switch json { + | Object(dict{"name": JSON.String(name), "email": JSON.String(email)}) => + Some({name, email}) + | _ => None + } } + ``` ```js -let d = { - A: 5, - B: 6 -}; - -d["A"] = -1; - -d["C"] = 0; +function decodeUser(json) { + if (typeof json !== "object" || json === null || Array.isArray(json)) { + return; + } + let name = json.name; + if (typeof name !== "string") { + return; + } + let email = json.email; + if (typeof email === "string") { + return { + name: name, + email: email + }; + } + +} ``` - \ No newline at end of file + diff --git a/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx b/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx index b6b0ed587..3913e767d 100644 --- a/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx +++ b/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx @@ -602,6 +602,63 @@ printStudents({ +### Match on Dictionaries + +You can pattern match on dictionaries just like you can on other ReScript data structures. + +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: + + +```res prelude +let d = dict{"A": 5, "B": 6} + +// We're expecting the `B` key to exist below, and `b` will be `int` in the match branch +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} +``` + +```js +let d = { + A: 5, + B: 6 +}; + +let b = d.B; + +let b$1 = b !== undefined ? b : undefined; +``` + + + +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: + + + +```res prelude +let d = dict{"A": 5, "B": 6} + +// We're pulling out `B` regardless of if it has a value or not, and therefore get `b` as `option` +let b = switch d { +| dict{"B": ?b} => b +} +``` + +```js +let d = { + A: 5, + B: 6 +}; + +let b = d.B; +``` + + + +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. + +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. ### Small Pitfall From b5fecd407c6909768932eb4c944dafceb343b8ff Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 5 May 2025 10:21:37 +0200 Subject: [PATCH 3/3] add dict to the syntax widget --- misc_docs/syntax/language_dict.mdx | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 misc_docs/syntax/language_dict.mdx diff --git a/misc_docs/syntax/language_dict.mdx b/misc_docs/syntax/language_dict.mdx new file mode 100644 index 000000000..73d396258 --- /dev/null +++ b/misc_docs/syntax/language_dict.mdx @@ -0,0 +1,51 @@ +--- +id: "dict" +keywords: ["dict"] +name: "dict" +summary: "This is the `dict{}` syntax" +category: "languageconstructs" +--- + +> Available in v12+ + +The `dict{}` syntax is used to represent [dictionaries](dict.md). It's used both when creating dicts, and when pattern matching on dicts. + +### Example + + + +```res +// Create a dict +let d = dict{"A": 5, "B": 6, "C": 7} + +// Pattern match on the full dict +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} + +// Destructure the dict +let dict{"C": ?c} = d +``` + +```js +let d = { + A: 5, + B: 6, + C: 7 +}; + +let b = d.B; + +let b$1 = b !== undefined ? b : undefined; + +let c = d.C; +``` + + + +### References + +* [Dictionaries](/docs/manual/latest/dict) + +