From 357e9c924335e1613437a7f74b28eb81dd1883c5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 3 May 2025 12:37:41 +0200 Subject: [PATCH 1/2] docs for variant type spreads in pattern matching --- .../pattern-matching-destructuring.mdx | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) 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..7c6e84bed 100644 --- a/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx +++ b/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx @@ -478,6 +478,165 @@ if (person1.TAG) { **Note:** Rescript versions < 9.0 had a `when` clause, not an `if` clause.  Rescript 9.0 changed `when` to `if`.  (`when` may still work, but is deprecated.) +### Match on subtype variants +You can refine a variant A to variant B using the variant type spread syntax in pattern matching. This is possible if variant B is a subtype of variant A. + +Let's look at an example: + + + +```res +type pets = Cat | Dog +type fish = Cod | Salmon +type animals = | ...pets | ...fish + +let greetPet = (pet: pets) => { + switch pet { + | Cat => Console.log("Hello kitty!") + | Dog => Console.log("Woof woof doggie!") + } +} + +let greetFish = (fish: fish) => { + switch fish { + | Cod => Console.log("Blub blub..") + | Salmon => Console.log("Blub blub blub blub..") + } +} + +let greetAnimal = (animal: animals) => { + switch animal { + | ...pets as pet => greetPet(pet) + | ...fish as fish => greetFish(fish) + } +} +``` +```js +function greetPet(pet) { + if (pet === "Cat") { + console.log("Hello kitty!"); + return; + } + console.log("Woof woof doggie!"); +} + +function greetFish(fish) { + if (fish === "Cod") { + console.log("Blub blub.."); + return; + } + console.log("Blub blub blub blub.."); +} + +function greetAnimal(animal) { + switch (animal) { + case "Cat" : + case "Dog" : + return greetPet(animal); + case "Cod" : + case "Salmon" : + return greetFish(animal); + } +} +``` + + +Let's break down what we did: +* Defined two different variants for pets and for fish +* Wrote a dedicated function per animal type to greet that particular type of animal +* Combined `pets` and `fish` into a main variant for `animals` +* Wrote a function that can greet any animal by _spreading_ each sub variant on its own branch, aliasing that spread to a variable, and passing that variable to the dedicated greet function for that specific type + +Notice how we're able to match on parts of the main variant, as long as the variants are compatible. + +The example above aliases the variant type spread to a variable so we can use it in our branch. But, you can just as easily match without aliasing if you don't care about the actual value for your branch: + + +```res +let isPet = (animal: animals) => { + switch animal { + | ...pets => Console.log("A pet!") + | _ => Console.log("Not a pet...") + } +} + +``` +```js +function isPet(animal) { + switch (animal) { + case "Cat" : + case "Dog" : + console.log("A pet!"); + return; + case "Cod" : + case "Salmon" : + console.log("Not a pet..."); + return; + } +} +``` + + +Similarily, if you want to get really advanced, you can even pull out a single variant constructor. This works with and without aliases. Example: + + + +```res +type dog = Dog +type pets = Cat | ...dog +type fish = Cod | Salmon +type animals = | ...pets | ...fish + +let isPet = (animal: animals) => { + switch animal { + | ...dog => Console.log("A dog!") + | _ => Console.log("Not a dog...") + } +} + +``` +```js +function isPet(animal) { + if (animal === "Dog") { + console.log("A dog!"); + return; + } + console.log("Not a dog..."); +} +``` + + +And, thanks to the rules of subtyping, the `Dog` constructor wouldn't _really_ need to be spread inside of the `pets` variant for this to work: + + + +```res +type pets = Cat | Dog +type fish = Cod | Salmon +type animals = | ...pets | ...fish + +// Notice `dog` isn't spread into the `pets` variant, but this still work due to subtyping +type dog = Dog + +let isPet = (animal: animals) => { + switch animal { + | ...dog => Console.log("A dog!") + | _ => Console.log("Not a dog...") + } +} + +``` +```js +function isPet(animal) { + if (animal === "Dog") { + console.log("A dog!"); + return; + } + console.log("Not a dog..."); +} +``` + + ### Match on Exceptions If the function throws an exception (covered later), you can also match on _that_, in addition to the function's normally returned values. From f6e92e24e468651da1c18ecee79ff41215cc3117 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 3 May 2025 14:17:39 +0200 Subject: [PATCH 2/2] links and touch ups --- .../manual/v12.0.0/pattern-matching-destructuring.mdx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 7c6e84bed..9983fab00 100644 --- a/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx +++ b/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx @@ -479,7 +479,7 @@ if (person1.TAG) { **Note:** Rescript versions < 9.0 had a `when` clause, not an `if` clause.  Rescript 9.0 changed `when` to `if`.  (`when` may still work, but is deprecated.) ### Match on subtype variants -You can refine a variant A to variant B using the variant type spread syntax in pattern matching. This is possible if variant B is a subtype of variant A. +You can refine a variant A to variant B using the [variant type spread syntax](variant.md#variant-type-spreads) in pattern matching. This is possible if variant B [is a subtype of](variant.md#coercion) variant A. Let's look at an example: @@ -549,7 +549,7 @@ Let's break down what we did: Notice how we're able to match on parts of the main variant, as long as the variants are compatible. -The example above aliases the variant type spread to a variable so we can use it in our branch. But, you can just as easily match without aliasing if you don't care about the actual value for your branch: +The example above aliases the variant type spread to a variable so we can use it in our branch. But, you can just as easily match without aliasing if you don't care about the value: ```res @@ -577,7 +577,7 @@ function isPet(animal) { ``` -Similarily, if you want to get really advanced, you can even pull out a single variant constructor. This works with and without aliases. Example: +Similarily, if you want to get advanced, you can even pull out a single variant constructor. This works with and without aliases. Example: @@ -615,7 +615,8 @@ type pets = Cat | Dog type fish = Cod | Salmon type animals = | ...pets | ...fish -// Notice `dog` isn't spread into the `pets` variant, but this still work due to subtyping +// Notice `dog` isn't spread into the `pets` variant, +// but this still work due to subtyping. type dog = Dog let isPet = (animal: animals) => {