Skip to content

Commit 97a3b52

Browse files
authored
Docs for variant type spreads in pattern matching (#1008)
* docs for variant type spreads in pattern matching * links and touch ups
1 parent 944d301 commit 97a3b52

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed

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

+160
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,166 @@ if (person1.TAG) {
478478

479479
**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.)
480480

481+
### Match on subtype variants
482+
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.
483+
484+
Let's look at an example:
485+
486+
<CodeTab labels={["ReScript", "JS Output"]}>
487+
488+
```res
489+
type pets = Cat | Dog
490+
type fish = Cod | Salmon
491+
type animals = | ...pets | ...fish
492+
493+
let greetPet = (pet: pets) => {
494+
switch pet {
495+
| Cat => Console.log("Hello kitty!")
496+
| Dog => Console.log("Woof woof doggie!")
497+
}
498+
}
499+
500+
let greetFish = (fish: fish) => {
501+
switch fish {
502+
| Cod => Console.log("Blub blub..")
503+
| Salmon => Console.log("Blub blub blub blub..")
504+
}
505+
}
506+
507+
let greetAnimal = (animal: animals) => {
508+
switch animal {
509+
| ...pets as pet => greetPet(pet)
510+
| ...fish as fish => greetFish(fish)
511+
}
512+
}
513+
```
514+
```js
515+
function greetPet(pet) {
516+
if (pet === "Cat") {
517+
console.log("Hello kitty!");
518+
return;
519+
}
520+
console.log("Woof woof doggie!");
521+
}
522+
523+
function greetFish(fish) {
524+
if (fish === "Cod") {
525+
console.log("Blub blub..");
526+
return;
527+
}
528+
console.log("Blub blub blub blub..");
529+
}
530+
531+
function greetAnimal(animal) {
532+
switch (animal) {
533+
case "Cat" :
534+
case "Dog" :
535+
return greetPet(animal);
536+
case "Cod" :
537+
case "Salmon" :
538+
return greetFish(animal);
539+
}
540+
}
541+
```
542+
</CodeTab>
543+
544+
Let's break down what we did:
545+
* Defined two different variants for pets and for fish
546+
* Wrote a dedicated function per animal type to greet that particular type of animal
547+
* Combined `pets` and `fish` into a main variant for `animals`
548+
* 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
549+
550+
Notice how we're able to match on parts of the main variant, as long as the variants are compatible.
551+
552+
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:
553+
<CodeTab labels={["ReScript", "JS Output"]}>
554+
555+
```res
556+
let isPet = (animal: animals) => {
557+
switch animal {
558+
| ...pets => Console.log("A pet!")
559+
| _ => Console.log("Not a pet...")
560+
}
561+
}
562+
563+
```
564+
```js
565+
function isPet(animal) {
566+
switch (animal) {
567+
case "Cat" :
568+
case "Dog" :
569+
console.log("A pet!");
570+
return;
571+
case "Cod" :
572+
case "Salmon" :
573+
console.log("Not a pet...");
574+
return;
575+
}
576+
}
577+
```
578+
</CodeTab>
579+
580+
Similarily, if you want to get advanced, you can even pull out a single variant constructor. This works with and without aliases. Example:
581+
582+
<CodeTab labels={["ReScript", "JS Output"]}>
583+
584+
```res
585+
type dog = Dog
586+
type pets = Cat | ...dog
587+
type fish = Cod | Salmon
588+
type animals = | ...pets | ...fish
589+
590+
let isPet = (animal: animals) => {
591+
switch animal {
592+
| ...dog => Console.log("A dog!")
593+
| _ => Console.log("Not a dog...")
594+
}
595+
}
596+
597+
```
598+
```js
599+
function isPet(animal) {
600+
if (animal === "Dog") {
601+
console.log("A dog!");
602+
return;
603+
}
604+
console.log("Not a dog...");
605+
}
606+
```
607+
</CodeTab>
608+
609+
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:
610+
611+
<CodeTab labels={["ReScript", "JS Output"]}>
612+
613+
```res
614+
type pets = Cat | Dog
615+
type fish = Cod | Salmon
616+
type animals = | ...pets | ...fish
617+
618+
// Notice `dog` isn't spread into the `pets` variant,
619+
// but this still work due to subtyping.
620+
type dog = Dog
621+
622+
let isPet = (animal: animals) => {
623+
switch animal {
624+
| ...dog => Console.log("A dog!")
625+
| _ => Console.log("Not a dog...")
626+
}
627+
}
628+
629+
```
630+
```js
631+
function isPet(animal) {
632+
if (animal === "Dog") {
633+
console.log("A dog!");
634+
return;
635+
}
636+
console.log("Not a dog...");
637+
}
638+
```
639+
</CodeTab>
640+
481641
### Match on Exceptions
482642

483643
If the function throws an exception (covered later), you can also match on _that_, in addition to the function's normally returned values.

0 commit comments

Comments
 (0)