From 198db7f71c7fcd64b524a32f59752a10652c44d6 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 15 Jul 2025 21:31:47 +0000
Subject: [PATCH 01/21] Add more outline code for part 4

---
 .../2025-07-12-extensible-datatypes-part-3.md |   2 +-
 .../2025-07-15-extensible-datatypes-part-4.md | 108 ++++++++++++++----
 2 files changed, 85 insertions(+), 25 deletions(-)

diff --git a/content/blog/2025-07-12-extensible-datatypes-part-3.md b/content/blog/2025-07-12-extensible-datatypes-part-3.md
index d214f42..aaaec5a 100644
--- a/content/blog/2025-07-12-extensible-datatypes-part-3.md
+++ b/content/blog/2025-07-12-extensible-datatypes-part-3.md
@@ -726,7 +726,7 @@ If any part of this explanation remains unclear, it might be helpful to paste th
 
 With the `BuildFrom` trait in place, we can now explore how CGP implements generalized **builder dispatchers** that enable flexible and reusable ways to assemble struct fields from various sources.
 
-## `BuildWithHandlers` Trait
+## `BuildWithHandlers` Provider
 
 In [earlier examples](/blog/extensible-datatypes-part-1/#builder-dispatcher), we used a utility called `BuildAndMergeOutputs` to combine outputs from multiple builder providers such as `BuildSqliteClient`, `BuildHttpClient`, and `BuildOpenAiClient`. Under the hood, `BuildAndMergeOutputs` is built upon a more fundamental dispatcher named `BuildWithHandlers`. The implementation of this dispatcher looks like the following:
 
diff --git a/content/blog/2025-07-15-extensible-datatypes-part-4.md b/content/blog/2025-07-15-extensible-datatypes-part-4.md
index 552ed4e..d0b07e9 100644
--- a/content/blog/2025-07-15-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-15-extensible-datatypes-part-4.md
@@ -13,9 +13,9 @@ draft = true
 
 # Implementation of Extensible Variants
 
-Now that we've covered how extensible records work in CGP, we can turn our attention to **extensible variants**. At first glance, it might seem like a completely different mechanism — but surprisingly, the approach used to implement extensible variants is very similar to that of extensible records. In fact, many of the same principles apply, just in the “opposite” direction.
+Now that we've covered how extensible records work in CGP, we can turn our attention to **extensible variants**. At first glance, it might seem like a completely different mechanism — but surprisingly, the approach used to implement extensible variants is very similar to that of extensible records. In fact, many of the same principles apply, just in the “opposite direction”.
 
-This close relationship between records and variants is rooted in **category theory**. In that context, records are known as **products**, while variants (or enums) are referred to as **sums** or **coproducts**. These terms highlight a deep **duality** between the two: just as products combine values, coproducts represent a choice among alternatives. CGP embraces this theoretical foundation and leverages it to create a unified design for both extensible records and extensible variants.
+This close relationship between records and variants is rooted in **category theory**. In that context, records are known as **products**, while variants (or enums) are referred to as **sums** or **coproducts**. These terms highlight a deep **duality** between the two: just as products *combine* values, coproducts represent a *choice* among alternatives. CGP embraces this theoretical foundation and leverages it to create a unified design for both extensible records and extensible variants.
 
 This duality is not just theoretical — it has practical implications for software architecture. Our design in CGP builds directly on prior research into **extensible data types**, particularly in the context of functional programming and type systems. For more on the background, see the paper on [Extensible Data Types](https://dl.acm.org/doi/10.1145/3290325), as well as this excellent [intro to category theory](https://bartoszmilewski.com/2015/01/07/products-and-coproducts/) by Bartosz Milewski.
 
@@ -100,7 +100,7 @@ If you need to represent more complex data in a variant, we recommend wrapping t
 
 ## Partial Variants
 
-Just as CGP supports partially constructed structs through *partial records*, it also enables *partial variants* to work with enums in a similarly flexible way. Partial variants allow you to pattern match on each variant of an enum incrementally, while safely excluding any variants that have already been handled. This makes it possible to build exhaustive and type-safe match chains that evolve over time.
+Just as CGP supports partially constructed structs through *partial records*, it also enables **partial variants** to work with **partially deconstructed** enums in a similarly flexible way. Partial variants allow you to pattern match on each variant of an enum incrementally, while safely excluding any variants that have already been handled. This makes it possible to build exhaustive and type-safe match chains that evolve over time.
 
 Consider the `Shape` enum we explored earlier. CGP would generate a corresponding `PartialShape` enum that represents the partial variant form of `Shape`:
 
@@ -142,7 +142,7 @@ This implementation makes it possible to work with a `Shape` value as a `Partial
 
 ## `IsVoid` Type Mapper
 
-The key distinction between partial records and partial variants lies in how we represent the absence of data. For partial variants, CGP introduces the `IsVoid` type mapper to indicate that a variant has already been extracted and is no longer available:
+The key distinction between partial records and partial variants lies in how we represent the **absence** of data. For partial variants, CGP introduces the `IsVoid` type mapper to indicate that a variant has already been extracted and is no longer available:
 
 ```rust
 pub enum Void {}
@@ -153,7 +153,7 @@ impl MapType for IsVoid {
 }
 ```
 
-The `Void` type is defined as an empty enum with no variants. This means that it is impossible to construct a value of type `Void`, and any code that attempts to match on a `Void` value will be statically unreachable. This makes it a safe and expressive way to model a variant that no longer exists in a given context.
+The `Void` type is defined as an empty enum with no variants. This means that it is **impossible** to construct a value of type `Void`, and any code that attempts to match on a `Void` value will be statically unreachable. This makes it a safe and expressive way to model a variant that no longer exists in a given context.
 
 Conceptually, `Void` serves the same purpose as Rust’s built-in [**never type**](https://doc.rust-lang.org/reference/types/never.html) or the [`Infallible`](https://doc.rust-lang.org/std/convert/enum.Infallible.html) type from the standard library. However, CGP defines `Void` explicitly to distinguish its special role in the context of extensible variants.
 
@@ -172,7 +172,7 @@ pub trait ExtractField<Tag> {
 }
 ```
 
-Just like `FromVariant` and `HasField`, the `ExtractField` trait takes a `Tag` type to identify the variant, and includes an associated `Value` type representing the variant’s inner data. Additionally, it defines a `Remainder` type, which represents the remaining variants that have not yet been matched.
+Just like `FromVariant` and `HasField`, the `ExtractField` trait takes a `Tag` type to identify the variant, and includes an associated `Value` type representing the variant’s inner data. Additionally, it defines a `Remainder` type, which represents the **remaining** variants that have not yet been matched.
 
 The `extract_field` method consumes the value and returns a `Result`, where a successful match yields the extracted `Value`, and a failed match returns the `Remainder`. Although this uses the `Result` type, the `Err` case is not really an error in the traditional sense — rather, it represents the remaining variants yet to be handled, much like how errors represent alternative outcomes in Rust.
 
@@ -207,20 +207,20 @@ Within the method body, we match on `self`. If the value is a `Circle`, we retur
 With `ExtractField`, we can now incrementally extract and match against variants in a safe and ergonomic way. Here’s an example of computing the area of a shape using this approach:
 
 ```rust
-let shape = Shape::Circle(Circle { radius: 5.0 });
-
-let area = match shape
-    .to_extractor() // PartialShape<IsPresent, IsPresent>
-    .extract_field(PhantomData::<symbol!("Circle")>)
-{
-    Ok(circle) => PI * circle.radius * circle.radius,
-    // PartialShape<IsVoid, IsPresent>
-    Err(remainder) => match remainder.extract_field(PhantomData::<symbol!("Rectangle")>) {
-        Ok(rectangle) => rectangle.width * rectangle.height,
-        // PartialShape<IsVoid, IsVoid>
-        // No need to match on `Err`
-    },
-};
+pub fn compute_area(shape: Shape) -> f64 {
+    match shape
+        .to_extractor() // PartialShape<IsPresent, IsPresent>
+        .extract_field(PhantomData::<symbol!("Circle")>)
+    {
+        Ok(circle) => PI * circle.radius * circle.radius,
+        // PartialShape<IsVoid, IsPresent>
+        Err(remainder) => match remainder.extract_field(PhantomData::<symbol!("Rectangle")>) {
+            Ok(rectangle) => rectangle.width * rectangle.height,
+            // PartialShape<IsVoid, IsVoid>
+            // No need to match on `Err`
+        },
+    }
+}
 ```
 
 In this example, we begin by converting the `Shape` value into a `PartialShape` with all variants present using `to_extractor`. We then call `extract_field` to try extracting the `Circle` variant. If successful, we compute the circle's area. If not, we receive a remainder value where the `Circle` variant is now marked as `IsVoid`. This remainder is then used to attempt extracting the `Rectangle` variant. If that succeeds, we compute the area accordingly.
@@ -229,6 +229,23 @@ By the time we reach the second `Err` case, the remainder has the type `PartialS
 
 What makes this approach so powerful is that the Rust type system can statically verify that it is impossible to construct a valid value for `PartialShape<IsVoid, IsVoid>`. We no longer need to write boilerplate `_ => unreachable!()` code or use runtime assertions. The type system ensures exhaustiveness and soundness entirely at compile time, enabling safer and more maintainable implementation of extensible variants.
 
+## Short Circuiting Remainder
+
+```rust
+pub fn compute_area(shape: Shape) -> Result<f64, Infallible> {
+    let remainder = shape
+        .to_extractor()
+        .extract_field(PhantomData::<symbol!("Circle")>)
+        .map(|circle| PI * circle.radius * circle.radius)⸮;
+
+    let remainder = remainder
+        .extract_field(PhantomData::<symbol!("Rectangle")>)
+        .map(|rectangle| rectangle.width * rectangle.height)⸮;
+
+    match remainder {}
+}
+```
+
 ## `FinalizeExtract` Trait
 
 While Rust’s type system can infer that a type like `PartialShape<IsVoid, IsVoid>` is uninhabitable, this inference only works when the compiler has access to the fully concrete type. To support this behavior more generically within CGP’s extensible variant system, the `FinalizeExtract` trait is introduced. This trait provides a mechanism to *discharge* an empty partial variant after all possible cases have been matched:
@@ -440,12 +457,11 @@ The extractor then proceeds to the next variant in the sum:
 * The updated `Source` is now `PartialShape<IsVoid, IsPresent>`, and the `Target` remains `ShapePlus`.
 * This source implements `ExtractField<symbol!("Rectangle")>`, yielding `Rectangle` as the value and `PartialShape<IsVoid, IsVoid>` as the final remainder.
 * The target once again supports `FromVariant<symbol!("Rectangle")>` using the matching `Rectangle` type.
-
-At the end of the chain, the `Void` variant is reached. The `FieldsExtractor` implementation for `Void` simply returns the remainder, which in this case is `PartialShape<IsVoid, IsVoid>`.
+* At the end of the chain, the `Void` variant is reached. The `FieldsExtractor` implementation for `Void` simply returns the remainder, which in this case is `PartialShape<IsVoid, IsVoid>`.
 
 What this process shows is that the `Upcast` operation works by examining each variant in the source type `Shape`, extracting each present value, and reinserting it into the target type `ShapePlus`. Once all fields have been processed, the remaining variants are guaranteed to be uninhabited. At that point, we can safely discharge the remainder using the `FinalizeExtract` trait.
 
-By breaking down the upcast into individual trait-driven steps over extensible variants, we can implement upcasting entirely in safe Rust. Even more importantly, this implementation is fully generic and reusable. We are not writing code solely for the purpose of supporting `Upcast` — instead, we are building a reusable foundation that also supports operations like `Downcast` and other generic manipulations over extensible variants.
+By breaking down the upcast into individual type-driven steps over extensible variants, we can implement upcasting entirely in safe Rust. Even more importantly, this implementation is fully generic and reusable. We are not writing code solely for the purpose of supporting `Upcast` — instead, we are building a reusable foundation that also supports operations like `Downcast` and other generic manipulations over extensible variants.
 
 ## `CanDowncast` Implementation
 
@@ -459,7 +475,7 @@ pub trait CanDowncast<Target> {
 }
 ```
 
-This trait is used to convert a value of an enum type into another enum that represents a *subset* of its variants. Unlike `CanUpcast`, which guarantees success by moving into a larger enum, `CanDowncast` may fail if the source contains variants not present in the target. To account for this, the trait includes an associated `Remainder` type to capture any unmatched variants, and the `downcast` method returns a `Result` that either yields the successfully downcasted value or the remainder.
+This trait is used to convert a value of an enum type into another enum that represents a *subset* of its variants. Unlike `CanUpcast`, which guarantees success by moving into a larger enum, `CanDowncast` may *fail* if the source contains variants not present in the target. To account for this, the trait includes an associated `Remainder` type to capture any unmatched variants, and the `downcast` method returns a `Result` that either yields the successfully downcasted value or the remainder.
 
 As with `CanUpcast`, we can define `CanDowncast` using a blanket implementation:
 
@@ -510,4 +526,48 @@ However, when the actual variant in this case is `Triangle`, which is not part o
 
 One of the most impressive aspects of both upcast and downcast is that they work seamlessly even when the source and target enums define their variants in entirely different orders. Because the trait implementations, such as `ExtractField`, operate in a generic and order-independent way, the correctness and behavior of casting are preserved regardless of variant ordering. This level of flexibility makes the CGP approach to extensible variants both powerful and practical for real-world use.
 
+# Visitor Dispatcher
+
+## `MatchWithHandlers` Provider
+
+```rust
+#[cgp_provider]
+impl<Context, Code, Input, Output, Remainder, Handlers> Computer<Context, Code, Input>
+    for MatchWithHandlers<Handlers>
+where
+    Input: HasExtractor,
+    DispatchMatchers<Handlers>:
+        Computer<Context, Code, Input::Extractor, Output = Result<Output, Remainder>>,
+    Remainder: FinalizeExtract,
+{
+    type Output = Output;
+
+    fn compute(_context: &Context, code: PhantomData<Code>, input: Input) -> Output {
+        let res = DispatchMatchers::compute(_context, code, input.to_extractor());
+
+        match res {
+            Ok(output) => output,
+            Err(remainder) => remainder.finalize_extract(),
+        }
+    }
+}
+```
+
+```rust
+pub type DispatchMatchers<Providers> = PipeMonadic<OkMonadic, Providers>;
+```
+
+## Example Use of `MatchWithHandlers`
+
+```rust
+let circle = Shape::Circle(Circle { radius: 5.0 });
+
+let area = MatchWithHandlers::<
+    Product![
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
+    ],
+>::compute(&(), PhantomData::<()>, circle);
+```
+
 # Conclusion

From 06374e854d2e0e80bcd8d5cee980c6f0909b6bdd Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Sat, 19 Jul 2025 15:01:26 +0000
Subject: [PATCH 02/21] Add section for explaining monad

---
 ...2025-07-20-extensible-datatypes-part-4.md} | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)
 rename content/blog/{2025-07-15-extensible-datatypes-part-4.md => 2025-07-20-extensible-datatypes-part-4.md} (86%)

diff --git a/content/blog/2025-07-15-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
similarity index 86%
rename from content/blog/2025-07-15-extensible-datatypes-part-4.md
rename to content/blog/2025-07-20-extensible-datatypes-part-4.md
index d0b07e9..f31c547 100644
--- a/content/blog/2025-07-15-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -528,8 +528,12 @@ One of the most impressive aspects of both upcast and downcast is that they work
 
 # Visitor Dispatcher
 
+With the traits for extensible variants defined, we can now turn our attention to how CGP implements generalized **visitor dispatchers**, similar to how the [builder dispatchers](/blog/extensible-datatypes-part-3/#builder-dispatcher) in our previous blog post was implemented.
+
 ## `MatchWithHandlers` Provider
 
+In the [examples in part 2](/blog/extensible-datatypes-part-2/#dispatching-eval), we have used dispatchers like `MatchWithValueHandlers` and `MatchWithValueHandlersRef` to dispatch the handling of variant values to different handlers based on the `Input` type. Under the hood, these dispatchers are built upon a more fundamental dispatcher called `MatchWithHandlers`, which is implemented as follows:
+
 ```rust
 #[cgp_provider]
 impl<Context, Code, Input, Output, Remainder, Handlers> Computer<Context, Code, Input>
@@ -553,10 +557,41 @@ where
 }
 ```
 
+The `MatchWithHandlers` provider is parameterized by a `Handlers` type, which represents a type-level list of visitor handlers that will process the variants from a generic `Input` type. Inside the `Computer` implementation, it requires `Input` to be an enum that implements `HasExtractor`.
+
+Inside the method body for `compute`, it first calls `input.to_extractor` to convert the input into partial variants, and then pass it to a lower level `DispatchMatchers<Handlers>` handler, which performs the actual dispatching logic and returns `Result<Output, Remainder>` as the output. The implementation then expects the `Remainder` that is returned to be inhabitable through `FinalizeExtract`. With that, it simply match on the return result to return the `Output`, and in the error case, it uses `finalize_extract()` to assert that the case cannot be reached.
+
+
+## `DispatchMatchers` Provider
+
+Recall that in the extensible builder implementation of `BuildWithHandlers`, we used `PipeHandlers` to form a pipeline of builders to gradually fill in the partial records returned by the previous builder handler. For the case of extensible visitors, the pipeline implementation is slightly different, but is still elegantly defined as follows:
+
 ```rust
 pub type DispatchMatchers<Providers> = PipeMonadic<OkMonadic, Providers>;
 ```
 
+What this means is that `DispatchMatchers` forms a **monadic** pipeline of visitor handlers, that is based on the monad implementation provided by `OkMonadic`.
+
+## What is a Monad?!
+
+At this point, most of the readers coming from Rust background would probably be confused on *what the heck is a [monad](https://wiki.haskell.org/Monad)*, and what does it have to do with implementing extensible visitors. So in this section, I will try to explain monads in much more simplified and less intimidating ways, using existing Rust concepts.
+
+To put it simply, a monad `M` is typically a container types that "contains" some value type `T` in the form of `M<T>`. We have in fact commonly used various monadic types in Rust, including `Option<T>`, `Result<T, E>`, and `impl Futures<Output = T>`.
+
+In addition to "containing" values, a monad provide a "bind" operation to "extract" the values out of it. In Rust, we have also frequently used this operation through the use of `?`, `.await`, or `.await?`.
+
+Learning all these, we can now understand `PipeMonadic` as simply asking CGP to automatically "apply" operators like `?`, `.await`, or `.await?` to the previous handler in the pipeline, to "extract" the result before passing it as an input to the next handler.
+
+The main strength of the monadic implementation is that we can not only use existing operators like `?`, but also perform similar operation on other types that have similar properties. This includes a composition of the existing types that we are already familiar with, such as `impl Futures<Output = Result<Result<Option<T>, E1>, E2>>`. With monads, you can imagine that we can now implement an operator that is roughly equivalent to `.await???` to "extract" the `T` value out of the nested container.
+
+For the case of `DispatchMatchers`, the "monad provider" we used is called `OkMonadic`. Essentially, this is the `⸮` operator that we have used in the pseudocode in the [`compute_area` example](#short-circuiting-remainder) to short circuit on the `Ok` variant and have a changing remainder type inside `Err`.
+
+So what we are really saying here is that, to implement `DispatchMatchers`, we just need to form a pipeline of handlers, where we use `⸮` to early return an `Ok(output)` from the returned `Result<Output, Remainder>` from the previous handler. Otherwise, we get back `Remainder` from `⸮`, and feed it as the input to the next handler.
+
+Thanks to `PipeMonadic` and `OkMonadic`, we do not need to implement this extraction manually. Instead, we just use the existing facilities provided by CGP to automatically implement this monadic pipeline for us.
+
+If you are still confused at this point, don't worry, as we will walk through a concrete example next. We will also have a blog post in the near future to explain about how CGP implements monads in Rust to support operations like `PipeMonadic`.
+
 ## Example Use of `MatchWithHandlers`
 
 ```rust

From 36f2abe41f887f4e019dd30c78e0dbee071538d6 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Sun, 20 Jul 2025 04:25:41 +0000
Subject: [PATCH 03/21] Write short circuiting remainder section

---
 .../2025-07-20-extensible-datatypes-part-4.md | 24 +++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index f31c547..53324f0 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -6,8 +6,6 @@ description = ""
 
 authors = ["Soares Chen"]
 
-draft = true
-
 +++
 
 
@@ -71,7 +69,7 @@ impl FromVariant<symbol!("Rectangle")> for Shape {
 
 This allows the `Shape` enum to be constructed generically using just the tag and value.
 
-## Limitations on Enum Shape
+## Restrictions on Enum Shape
 
 To ensure ergonomics and consistency, CGP restricts the kinds of enums that can derive `FromVariant`. Specifically, supported enums must follow the **sums of products** pattern—each variant must contain *exactly one unnamed field*.
 
@@ -94,7 +92,7 @@ pub enum Shape {
 }
 ```
 
-These more complex variants are not supported because they would make it harder to represent variant fields as simple types, which would, in turn, lead to less ergonomic APIs. By limiting each variant to a single unnamed field, CGP ensures that types like `FromVariant::Value` remain straightforward and intuitive.
+These more complex variants are not supported because they would make it harder to represent variant fields as simple types, which would, in turn, lead to less ergonomic APIs. By restricting each variant to a single unnamed field, CGP ensures that types like `FromVariant::Value` remain straightforward and intuitive.
 
 If you need to represent more complex data in a variant, we recommend wrapping that data in a dedicated struct. This way, you can still take advantage of CGP's extensible variant system while maintaining type clarity and composability.
 
@@ -231,6 +229,12 @@ What makes this approach so powerful is that the Rust type system can statically
 
 ## Short Circuiting Remainder
 
+The earlier implementation of `compute_area` involves the use of nested `match` on the `Result` returned from `extract_field`. If you are familiar with the use of `?` on `Result`, you might wonder why don't we use it in the method implementation.
+
+The main reason `?` wouldn't work with our example is because we want to short circuit and return the `Ok` variant early, while the remainder type in the `Err` variant changes in each call to `extract_field`. This is the *inverse* of how we usually expect `Result` to be used, where the `Err` variant is returned early, while the type in the `Ok` variant changes with each method call.
+
+To really simplify the code, we can imagine it to be written as the following pseudocode:
+
 ```rust
 pub fn compute_area(shape: Shape) -> Result<f64, Infallible> {
     let remainder = shape
@@ -246,6 +250,18 @@ pub fn compute_area(shape: Shape) -> Result<f64, Infallible> {
 }
 ```
 
+In the above pseudocode, we *invent* a new operator `⸮`, which behaves in the opposite way as `?`. It short circuits and return the `Ok` variant early, or binds the remainder value in the `Err` variant to the `remainder` variable in the `let` binding.
+
+In the example, we use `.map` to map the `Ok` variant from the shape to the `f64` area result, and return early through `⸮`. Otherwise, the `remainder` variable gradually shrinks, until it becomes inhabitable in the end. We then use `match remainder {}` to statically assert that the case cannot be reach.
+
+In other words, our new `compute_area` function don't really "return" anything in the `Err` variant. But to complete the type signature, we use `Result<f64, Infallible>` as the return type, with `Infallible` representing that the operation can never fail with an `Err` variant.
+
+At this point, some readers may point out that there are other ways to simplify the Rust code without introducing the `⸮` operator. For example, we could make `extract_field` return `Result<Remainder, Value>` so that we can short circuit `Err(Value)` through `?`. However, doing so may be confusing, as it makes more sense that `Remainder` is the "exceptional" case here.
+
+Furthermore, we introduce `⸮` as an exercise for you to think about how it relates to other existing operators such as `?`, `.await`, and `.await?`. In practice, we won't need Rust to officially support `⸮`, nor would we need to manually implement functions like `compute_area`. Instead, the extensible visitor pattern would apply something like `⸮` for us in the implementation to automatically implement `compute_area` for us.
+
+We will revisit this topic in the later section about the implementation of extensible visitor. For now, let's move on to how we finalize an empty remainder.
+
 ## `FinalizeExtract` Trait
 
 While Rust’s type system can infer that a type like `PartialShape<IsVoid, IsVoid>` is uninhabitable, this inference only works when the compiler has access to the fully concrete type. To support this behavior more generically within CGP’s extensible variant system, the `FinalizeExtract` trait is introduced. This trait provides a mechanism to *discharge* an empty partial variant after all possible cases have been matched:

From 64ccd79eb47f1672552ac6bf230d5109fd6dfcc8 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Sun, 20 Jul 2025 15:41:12 +0000
Subject: [PATCH 04/21] Add example use of MatchWithHandlers

---
 .../2025-07-20-extensible-datatypes-part-4.md | 65 +++++++++++++++++--
 1 file changed, 61 insertions(+), 4 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 53324f0..d4baf6f 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -610,15 +610,72 @@ If you are still confused at this point, don't worry, as we will walk through a
 
 ## Example Use of `MatchWithHandlers`
 
+To see how we can use `MatchWithHandlers` directly, let's revisit the earlier example of computing the area of `Shape`. We first define two separate `Computer` providers to compute the `Circle` and `Rectangle` variants of shape as follows:
+
+```rust
+#[cgp_computer]
+fn circle_area(circle: Circle) -> f64 {
+    PI * circle.radius * circle.radius
+}
+
+#[cgp_computer]
+fn rectangle_area(rectangle: Rectangle) -> f64 {
+    rectangle.width * rectangle.height
+}
+```
+
+We use the `#[cgp_computer]` macro to turn our pure functions into context-generic providers that can be referenced as types. Behind the scene, `#[cgp_computer]` generates a `Computer` implementation that looks like:
+
+```rust
+#[cgp_provider]
+impl<Context, Code> Computer<Context, Code, Circle> for CircleArea {
+    type Output = f64;
+
+    fn compute(_context: &Context, _code: PhantomData<Code>, input: Circle) -> f64 {
+        circle_area(input)
+    }
+}
+```
+
+As we can see, `#[cgp_computer]` gives us the convenience that we can define pure `Computer` instances more easily, by just writing them as functions. Since we ignore that `Context` and `Code` types, we can instantiate the provider to work with any `Context` or `Code` that we provide to it.
+
+We can then pass `CircleArea` and `RectangleArea` to `MatchWithHandlers` to compute the area of `Shape` as follows:
+
 ```rust
-let circle = Shape::Circle(Circle { radius: 5.0 });
+let shape = Shape::Circle(Circle { radius: 5.0 });
 
 let area = MatchWithHandlers::<
     Product![
-        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
-        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<CircleArea>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<RectangleArea>>,
     ],
->::compute(&(), PhantomData::<()>, circle);
+>::compute(&(), PhantomData::<()>, shape);
 ```
 
+Instead of passing the providers to `MatchWithHandlers` directly, they are wrapped by some other constructs. First, the `ExtractFieldAndHandle` handler extracts the variant value from the specified tag, e.g. `symbol!("Circle")`, and pass it to the inner handler `HandleFieldValue<CircleArea>`.
+
+The inner input is given in the form `Field<symbol("Circle"), Circle>`, and the `HandleFieldValue` handler extracts the `Circle` value from `Field`, and passes it as the input to `CircleArea`.
+
+As a whole, we expect the instantiated `MatchWithHandlers` implements the `Computer` trait, and we call `compute` on it with `()` as the `Context` type and also the `Code` type. This works, because the `Computer` instances that we have defined earlier with `#[cgp_computer]` could work with any `Context` and `Code` type.
+
+Behind the scene, `MatchWithHandlers` desugars the call to roughly something like the following pseudocode:
+
+```rust
+let remainder = shape.to_extractor();
+
+let remainder = remainder
+    .extract_field(symbol!("Circle"))
+    .map(|circle| CircleArea::compute(&(), PhantomData::<()>, circle))⸮;
+
+let remainder = remainder
+    .extract_field(symbol!("Rectangle"))
+    .map(|rectangle| Rectangle::compute(&(), PhantomData::<()>, rectangle))⸮;
+
+remainder.finalize_extract();
+```
+
+That is, `MatchwithHandlers` now performs the same `⸮` [short circuit operation](#short-circuiting-remainder) that we hand-implemented earlier. And for each mapping of the `Ok` result in the extraction, we instead use the `Computer` providers to perform the area calculations.
+
+From the expansion, we can see the amount of boilerplate code that `MatchWithHandlers` has implemented for us. This in turn is implemented simply as a monadic pipeline through `PipeMonadic`, with `OkMonadic` being "passed" as the `⸮` operator that we have used in this pseudocode expansion.
+
 # Conclusion

From 28e99fa0aedefa414eef24a1071bc40a2b7b952b Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Mon, 28 Jul 2025 13:04:11 +0000
Subject: [PATCH 05/21] Finish drafting implementation of visitor dispatcher

---
 .../2025-07-12-extensible-datatypes-part-3.md |   2 +-
 .../2025-07-20-extensible-datatypes-part-4.md | 328 +++++++++++++++++-
 2 files changed, 319 insertions(+), 11 deletions(-)

diff --git a/content/blog/2025-07-12-extensible-datatypes-part-3.md b/content/blog/2025-07-12-extensible-datatypes-part-3.md
index aaaec5a..19f730a 100644
--- a/content/blog/2025-07-12-extensible-datatypes-part-3.md
+++ b/content/blog/2025-07-12-extensible-datatypes-part-3.md
@@ -965,7 +965,7 @@ With this setup, `BuildAndMergeOutputs` can now be used like any other CGP provi
 
 The key benefit of this pattern is that it avoids boilerplate while preserving type safety. Whenever a provider's implementation is simply a thin wrapper around another provider with some added constraints, it's much more convenient to use `DelegateComponent` via `delegate_components!` than to implement the provider trait manually.
 
-## Type-Level CGP Metaprogramming
+## Type-Level Metaprogramming
 
 The technique we just explored — wrapping providers and using `delegate_components!` — can be seen as a form of **metaprogramming** in CGP. Here, we’re leveraging **type-level programming** not just within CGP’s core component abstractions like `DelegateComponent`, but also as a tool for *programmatically defining component wiring* through the use of generic parameters and trait constraints.
 
diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index d4baf6f..ea7342c 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -575,7 +575,7 @@ where
 
 The `MatchWithHandlers` provider is parameterized by a `Handlers` type, which represents a type-level list of visitor handlers that will process the variants from a generic `Input` type. Inside the `Computer` implementation, it requires `Input` to be an enum that implements `HasExtractor`.
 
-Inside the method body for `compute`, it first calls `input.to_extractor` to convert the input into partial variants, and then pass it to a lower level `DispatchMatchers<Handlers>` handler, which performs the actual dispatching logic and returns `Result<Output, Remainder>` as the output. The implementation then expects the `Remainder` that is returned to be inhabitable through `FinalizeExtract`. With that, it simply match on the return result to return the `Output`, and in the error case, it uses `finalize_extract()` to assert that the case cannot be reached.
+Inside the method body for `compute`, it first calls `input.to_extractor()` to convert the input into partial variants, and then pass it to a lower level `DispatchMatchers<Handlers>` handler, which performs the actual dispatching logic and returns `Result<Output, Remainder>` as the output. The implementation then expects the `Remainder` that is returned to be inhabitable through `FinalizeExtract`. With that, it simply match on the return result to return the `Output`, and in the error case, it uses `finalize_extract()` to assert that the case cannot be reached.
 
 
 ## `DispatchMatchers` Provider
@@ -592,7 +592,7 @@ What this means is that `DispatchMatchers` forms a **monadic** pipeline of visit
 
 At this point, most of the readers coming from Rust background would probably be confused on *what the heck is a [monad](https://wiki.haskell.org/Monad)*, and what does it have to do with implementing extensible visitors. So in this section, I will try to explain monads in much more simplified and less intimidating ways, using existing Rust concepts.
 
-To put it simply, a monad `M` is typically a container types that "contains" some value type `T` in the form of `M<T>`. We have in fact commonly used various monadic types in Rust, including `Option<T>`, `Result<T, E>`, and `impl Futures<Output = T>`.
+To put it simply, a monad `M` is typically a container types that "contains" some value type `T` in a "shape" that looks like `M<T>`. We have in fact commonly used various monadic types in Rust, including `Option<T>`, `Result<T, E>`, and `impl Futures<Output = T>`.
 
 In addition to "containing" values, a monad provide a "bind" operation to "extract" the values out of it. In Rust, we have also frequently used this operation through the use of `?`, `.await`, or `.await?`.
 
@@ -600,6 +600,8 @@ Learning all these, we can now understand `PipeMonadic` as simply asking CGP to
 
 The main strength of the monadic implementation is that we can not only use existing operators like `?`, but also perform similar operation on other types that have similar properties. This includes a composition of the existing types that we are already familiar with, such as `impl Futures<Output = Result<Result<Option<T>, E1>, E2>>`. With monads, you can imagine that we can now implement an operator that is roughly equivalent to `.await???` to "extract" the `T` value out of the nested container.
 
+### `OkMonadic` Monad Provider
+
 For the case of `DispatchMatchers`, the "monad provider" we used is called `OkMonadic`. Essentially, this is the `⸮` operator that we have used in the pseudocode in the [`compute_area` example](#short-circuiting-remainder) to short circuit on the `Ok` variant and have a changing remainder type inside `Err`.
 
 So what we are really saying here is that, to implement `DispatchMatchers`, we just need to form a pipeline of handlers, where we use `⸮` to early return an `Ok(output)` from the returned `Result<Output, Remainder>` from the previous handler. Otherwise, we get back `Remainder` from `⸮`, and feed it as the input to the next handler.
@@ -624,6 +626,8 @@ fn rectangle_area(rectangle: Rectangle) -> f64 {
 }
 ```
 
+### `#[cgp_computer]` Macro
+
 We use the `#[cgp_computer]` macro to turn our pure functions into context-generic providers that can be referenced as types. Behind the scene, `#[cgp_computer]` generates a `Computer` implementation that looks like:
 
 ```rust
@@ -639,26 +643,33 @@ impl<Context, Code> Computer<Context, Code, Circle> for CircleArea {
 
 As we can see, `#[cgp_computer]` gives us the convenience that we can define pure `Computer` instances more easily, by just writing them as functions. Since we ignore that `Context` and `Code` types, we can instantiate the provider to work with any `Context` or `Code` that we provide to it.
 
-We can then pass `CircleArea` and `RectangleArea` to `MatchWithHandlers` to compute the area of `Shape` as follows:
+### `ComputeShapeArea` Handler
 
-```rust
-let shape = Shape::Circle(Circle { radius: 5.0 });
+With `CircleArea` and `RectangleArea` defined, we can now define a `ComputeShapeArea` handler as a type alias that uses `MatchWithHandlers` as follows:
 
-let area = MatchWithHandlers::<
+```rust
+pub type ComputeShapeArea = MatchWithHandlers::<
     Product![
         ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<CircleArea>>,
         ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<RectangleArea>>,
     ],
->::compute(&(), PhantomData::<()>, shape);
+>;
 ```
 
 Instead of passing the providers to `MatchWithHandlers` directly, they are wrapped by some other constructs. First, the `ExtractFieldAndHandle` handler extracts the variant value from the specified tag, e.g. `symbol!("Circle")`, and pass it to the inner handler `HandleFieldValue<CircleArea>`.
 
-The inner input is given in the form `Field<symbol("Circle"), Circle>`, and the `HandleFieldValue` handler extracts the `Circle` value from `Field`, and passes it as the input to `CircleArea`.
+Next, the inner input is given in the form `Field<symbol("Circle"), Circle>`, and the `HandleFieldValue` handler extracts the `Circle` value from `Field`, and passes it as the input to `CircleArea`. We will walk through the implementation of `ExtractFieldAndHandle` and `HandleFieldValue` right after this, but first, let's take a look at how `ComputeShapeArea` can be used.
+
+As a whole, we expect the instantiated `MatchWithHandlers` implements the `Computer` trait, and we call `compute` on it with `()` as the `Context` type and also the `Code` type as follows:
+
+```rust
+let shape = Shape::Circle(Circle { radius: 5.0 });
+let area = ComputeShapeArea::compute(&(), PhantomData::<()>, shape);
+```
 
-As a whole, we expect the instantiated `MatchWithHandlers` implements the `Computer` trait, and we call `compute` on it with `()` as the `Context` type and also the `Code` type. This works, because the `Computer` instances that we have defined earlier with `#[cgp_computer]` could work with any `Context` and `Code` type.
+This works, because the `Computer` instances that we have defined earlier with `#[cgp_computer]` could work with any `Context` and `Code` type.
 
-Behind the scene, `MatchWithHandlers` desugars the call to roughly something like the following pseudocode:
+Behind the scene, `MatchWithHandlers` implements `ComputeShapeArea` roughly as something like the following pseudocode:
 
 ```rust
 let remainder = shape.to_extractor();
@@ -678,4 +689,301 @@ That is, `MatchwithHandlers` now performs the same `⸮` [short circuit operatio
 
 From the expansion, we can see the amount of boilerplate code that `MatchWithHandlers` has implemented for us. This in turn is implemented simply as a monadic pipeline through `PipeMonadic`, with `OkMonadic` being "passed" as the `⸮` operator that we have used in this pseudocode expansion.
 
+## `ExtractFieldAndHandle`
+
+To understand better how the `MatchWithHandlers` example earlier works, we will take a look at how the `ExtractFieldAndHandle` provider is implemented:
+
+```rust
+#[cgp_provider]
+impl<Context, Code, Input, Tag, Value, Provider, Output, Remainder>
+    Computer<Context, Code, Input>
+    for ExtractFieldAndHandle<Tag, Provider>
+where
+    Input: ExtractField<Tag, Value = Value, Remainder = Remainder>,
+    Provider: Computer<Context, Code, Field<Tag, Value>, Output = Output>,
+{
+    type Output = Result<Output, Remainder>;
+
+    fn compute(
+        context: &Context,
+        tag: PhantomData<Code>,
+        input: Input,
+    ) -> Result<Output, Remainder> {
+        let value = input.extract_field(PhantomData::<Tag>)?;
+        let output = Provider::compute(context, tag, value.into());
+        Ok(output)
+    }
+}
+```
+
+The type signature for `ExtractFieldAndHandle` may look complicated, but what is does is pretty simple. Given some partial variants `Input`, the handler tries to use `ExtractField` to extract a variant with the specified `Tag` from the partial variants. If the variant extraction is successful, it feeds the tagged field value `Field<Tag, Value>` to the inner `Provider` to process the variant. Otherwise, it returns the remainder as an `Err`, so that it will be handled by the next handler in the monadic pipeline.
+
+It is worth noting that we pass a tagged `Field<Tag, Value>` to the inner `Provider`, instead of a bare `Value`, so that the provider is given an option to handle multiple variants with the same `Value` type based on the variant `Tag`. As an example, consider the following enum:
+
+```rust
+pub enum FooBar {
+    Foo(u64),
+    Bar(u64),
+}
+```
+
+Even though both the `Foo` and `Bar` variants have `u64` as the `Value` type, they will be passed from `ExtractFieldAndHandle` as `Field<symbol!("Foo"), u64>` and `Field<symbol!("Bar"), u64>` to the inner provider. This means that the inner provider may choose to handle the `Foo` and `Bar` variants differently, by matching on the field `Tag`.
+
+### `HandleFieldValue`
+
+The tagged `Field` input from `ExtractFieldAndHandle` is useful for cases when there are multiple variants with the same `Value` type. However, in simpler cases such as `Shape`, we often don't care about the variant `Tag` and would like to process the value directly. To simplify the process, we can use the `HandleFieldValue` wrapper to "peel" off the `Field` wrapper from the `Input`, and get the `Value` directly as an input to the inner provider. The `HandleFieldValue` is implemented as follows:
+
+```rust
+#[cgp_provider]
+impl<Context, Code, Tag, Input, Output, Provider> Computer<Context, Code, Field<Tag, Input>>
+    for HandleFieldValue<Provider>
+where
+    Provider: Computer<Context, Code, Input, Output = Output>,
+{
+    type Output = Output;
+
+    fn compute(
+        context: &Context,
+        tag: PhantomData<Code>,
+        input: Field<Tag, Input>,
+    ) -> Self::Output {
+        Provider::compute(context, tag, input.value)
+    }
+}
+```
+
+As we can see, `HandleFieldValue` simply works with any input type in the form `Field<Tag, Input>`, unwrap the input value from `Field`, and then pass it to the inner provider.
+
+### Revisiting `ComputeShapeArea`
+
+Now that we have understood how `ExtractFieldAndHandle` and `HandleFieldValue` are implemented, let's walk through again what really happens when we use `MatchWithHandlers` in `ComputeShapeArea`:
+
+```rust
+pub type ComputeShapeArea = MatchWithHandlers::<
+    Product![
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<CircleArea>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<RectangleArea>>,
+    ],
+>;
+```
+
+- `MatchWithHandlers` uses `HasExtractor` to convert `Shape` into `PartialShape<IsPresent, IsPresent>`, and passes it to `ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<CircleArea>>`.
+- `ExtractFieldAndHandle` uses `ExtractField<symbol("Circle")>` to extract the `Circle` variant from `PartialShape<IsPresent, IsPresent>`.
+    - If successful, the value is passed as `Field<symbol("Circle"), Circle>` to `HandleFieldValue<CircleArea>`.
+        - `HandleFieldValue<CircleArea>` extracts the `Circle` value from `Field<symbol("Circle"), Circle>`, and passes it to `CircleArea`.
+    - Otherwise, the remainder `PartialShape<IsVoid, IsPresent>` is returned as error.
+- `ExtractFieldAndHandle` uses `ExtractField<symbol("Rectangle")>` to extract the `Rectangle` variant from `PartialShape<IsVoid, IsPresent>`.
+    - If successful, the value is passed as `Field<symbol("Rectangle"), Rectangle>` to `HandleFieldValue<RectangleArea>`.
+        - `HandleFieldValue<RectangleArea>` extracts the `Circle` value from `Field<symbol("Rectangle"), Rectangle>`, and passes it to `RectangleArea`.
+    - Otherwise, the remainder `PartialShape<IsVoid, IsVoid>` is returned as error.
+- `MatchWithHandlers` uses `FinalizeExtract` on `PartialShape<IsVoid, IsVoid>` to discharge the impossible remainder.
+
+
+## Unifying Variant Value Handlers
+
+At this point, we have seen how `MatchWithHandlers` can be used as a low-level construct for implementing extensible visitors. However, `MatchWithHandlers` requires the handler of each variant to be explicitly provided in the list of handlers given to it. We will now look at how higher-level handlers like `MatchWithValueHandlers` can be implemented to automatically derive the list of variant handlers to be passed to `MatchWithHandlers`.
+
+But before we can implement `MatchWithValueHandlers`, we first need to slightly modify the variant handler for all entries in `MatchWithHandlers`, such that the same handler is used for all variants:
+
+```rust
+pub type ComputeShapeArea = MatchWithHandlers<
+    Product![
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
+    ],
+>;
+```
+
+In the updated definition, we use `ComputeArea` instead of `CircleArea` and `RectangleArea` to compute the area for *both* `Circle` and `Rectangle`. By doing so, we can see that `ComputeShapeArea` now has a unified pattern of `ExtractFieldAndHandle<Tag, HandleFieldValue<ComputeArea>>` for each of its entry. And we can now build further abstraction to simplify the repeating patterns.
+
+But before we do that, let's look at how `ComputeArea` can be implemented. For the case of `Shape`, as well of for many use cases of extensible variants, we can just define a regular Rust trait for computing the area of each shape variant:
+
+```rust
+pub trait HasArea {
+    fn area(self) -> f64;
+}
+
+impl HasArea for Circle {
+    fn area(self) -> f64 {
+        PI * self.radius * self.radius
+    }
+}
+
+impl HasArea for Rectangle {
+    fn area(self) -> f64 {
+        self.width * self.height
+    }
+}
+```
+
+The definition and implementation of `HasArea` is pretty straightforward, with it providing an `area` method to compute the area for each shape variant. Crucially, we do *not* manually implement `HasArea` for `Shape`, as the point of extensible variants is to allow us to skip the boilerplate required to manually call the `area` method for each of the variant in `Shape`.
+
+Although `HasArea` is a vanilla Rust trait, we can pretty easily implement a `Computer` provider for it as follows:
+
+```rust
+#[cgp_computer]
+fn compute_area<T: HasArea>(shape: T) -> f64 {
+    shape.area()
+}
+```
+
+Here, we use `#[cgp_computer]` on a `compute_area` function that is generic over any type that implements `HasArea`, and the implementation simply calls the `area` method. With this wrapper function, we now also have `ComputeArea` generated, which can be used in `ComputeShapeArea`.
+
+### `ToFieldsHandler`
+
+To simplify the implementation of `ComputeShapeArea`, we first need to figure a way to automatically generate the extractors given to `MatchWithHandlers`, which is:
+
+```rust
+Product![
+    ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
+    ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
+]
+```
+
+Recall that `Shape` also implements [`HasFields`](#hasfields-implementation), which is in the form:
+
+```rust
+Sum![
+    Field<symbol!("Circle"), Circle>,
+    Field<symbol!("Rectangle"), Rectangle>,
+]
+```
+
+This means that all we have to do is to extract the tags from `Shape::Fields`, and replace each entry with `ExtractFieldAndHandle`. We can perform this transformation at the type level, by using a helper trait `ToFieldHandlers` that is defined as follows:
+
+```rust
+pub trait ToFieldHandlers<Provider> {
+    type Handlers;
+}
+
+impl<Tag, Value, RestFields, Provider> ToFieldHandlers<Provider>
+    for Either<Field<Tag, Value>, RestFields>
+where
+    RestFields: ToFieldHandlers<Provider>,
+{
+    type Handlers = Cons<ExtractFieldAndHandle<Tag, Provider>, RestFields::Handlers>;
+}
+
+impl<Provider> ToFieldHandlers<Provider> for Void {
+    type Handlers = Nil;
+}
+```
+
+At a high level, `ToFieldHandlers` works by iterating through each entry in a type-level sum, replace each entry that is in the shape `Field<Tag, Value>` with `ExtractFieldAndHandle<Tag, Provider>`, and convert the result to a type-level list.
+
+Using `ToFieldHandlers`, we can now generate the arguments to `MatchWithHandlers` as something like follows:
+
+```rust
+pub type ComputeShapeArea = MatchWithHandlers<
+    <<Shape as HasFields>::Fields as
+        ToFieldHandlers<HandleFieldValue<ComputeArea>>
+    >::Handlers
+>;
+```
+
+Although this version of `ComputeShapeArea` may look confusing, we won't be writing something like this in practice. Instead, the example definition is to demonstrate the transformation that is happening behind the scene to automatically generate the list of variant handlers from `Shape` to be given to `MatchWithHandlers`.
+
+### `HasFieldHandlers`
+
+Using `ToFieldHandlers`, there is a two-step process to generate the variant handlers from `Shape` - first, we need to get `Shape`'s fields from `HasFields`, and then we need to apply `ToFieldHandlers` to the `Fields` type. We can further simplify the use through another helper trait called `HasFieldHandlers`, which is defined as follows:
+
+```rust
+pub trait HasFieldHandlers<Provider> {
+    type Handlers;
+}
+
+impl<Context, Fields, Provider> HasFieldHandlers<Provider> for Context
+where
+    Context: HasFields<Fields = Fields>,
+    Fields: ToFieldHandlers<Provider>,
+{
+    type Handlers = Fields::Handlers;
+}
+```
+
+Essentially, `HasFieldHandlers` combines both the constraints for `HasFields` and `ToFieldHandlers`, so that we can use it as one non-nested constraint definition:
+
+```rust
+pub type ComputeShapeArea = MatchWithHandlers<
+    <Shape as HasFieldHandlers<HandleFieldValue<ComputeArea>>>::Handlers
+>;
+```
+
+Using `HasFieldHandlers`, the definition of `ComputeShapeArea` becomes significantly simplified. We just need to use `HasFieldHandlers` to generate the variant handlers for `Shape`, and then pass that to `MatchWithHandlers`.
+
+In fact, if we look at the definition more carefully, we can apply the same pattern to be used on any `Input` type other than `Shape`, and any `Computer` provider other than `ComputeArea`.
+
+
+## `MatchWithValueHandlers`
+
+The traits `HasFieldHandlers` and `ToFieldHandlers` serve as the helpers for us to perform *type-level metaprogramming* for us to implement high-level visitor dispatchers such as `MatchWithValueHandlers`, which is defined as follows:
+
+```rust
+pub type MatchWithValueHandlers<Provider = UseContext> =
+    UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<Provider>>>;
+
+delegate_components! {
+    <Input: HasFieldHandlers<Provider>, Provider>
+    new MatchWithFieldHandlersInputs<Provider> {
+        Input: MatchWithHandlers<Input::Handlers>
+    }
+}
+```
+
+The way we implement `MatchWithValueHandlers` is similar to how we implemented the builder dispatchers using [type-level metaprogramming in part 3](/blog/extensible-datatypes-part-3/#type-level-cgp-metaprogramming). The way it works is as follows: first, `MatchWithValueHandlers` is parameterized by a `Provider` that is expected to implement `Computer`, such as `ComputeArea`. The `Provider` parameter has a default `UseContext` value, which uses `CanCompute` from the context to perform the computation if no explicit `Computer` is provided.
+
+The implementation of `MatchWithValueHandlers` is simply a type alias to use `UseInputDelegate` to dispatch the `Input` type given in `Computer` to `MatchWithFieldHandlersInputs`, which contains a *generic* type-level map from `Input` to `MatchWithHandlers`.
+
+The implementation of `MatchWithFieldHandlersInputs` is defined through `delegate_components!`, with it having a generic entry for any `Input` that implements `HasFieldHandlers<Provider>`. It then simply delegates the provider for that input to `MatchWithHandlers<Input::Handlers>`.
+
+### Example Instantiation of `MatchWithValueHandlers`
+
+Due to the advanced use of type-level metaprogramming, it may be challenging to understand how `MatchWithValueHandlers` and `MatchWithFieldHandlersInputs` works, especially when you read it for the first time. We will try to ease the understanding by looking at how this is used with `Shape`. First, the `ComputeShapeArea` definition can now be simplified to:
+
+```rust
+pub type ComputeShapeArea = MatchWithValueHandlers<ComputeArea>;
+```
+
+As we can see, `ComputeShapeArea` becomes simply a use of `MatchWithValueHandlers` with `ComputeArea`. The definition does not even include any reference of `Shape`, because it can now work with *any* compatible input type, including `Shape` and `ShapePlus`.
+
+Behind the scene, the implementation of `ComputeShapeArea` is now redirected to `MatchWithHandlers` with the following steps:
+
+- The type alias `MatchWithValueHandlers<ComputeArea>` is expanded to `UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>>`.
+- When the `Input` type is `Shape`, `UseInputDeleate` looks up the `DelegateComponent` entry for `Shape` in `MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>`.
+- `MatchWithFieldHandlersInputs` contains an entry for `Shape`, given that `Shape` implements `HasFieldHandlers<HandleFieldValue<ComputeArea>>`.
+- As demonstrated earlier, the `HasFieldHandlers` implementation for `Shape` expands to:
+    ```rust
+    Product![
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
+    ]
+    ```
+- This expanded type is now passed to `MatchWithHandlers`, which implements the visitor dispatch as we have walked through in earlier sections.
+
+## Implementing `HasArea` for `Shape`
+
+Using `MatchWithValueHandlers`, it becomes trivial to implement `HasArea` for `Shape` as follows:
+
+```rust
+impl HasArea for Shape {
+    fn area(self) -> f64 {
+        MatchWithValueHandlers::<ComputeArea>::compute(&(), PhantomData::<()>, self)
+    }
+}
+```
+
+Just as with the earlier example, since `ComputeArea` is generic over any `Context` and `Code` type, we use `()` for both the arguments in `compute`. Aside from the boilerplate, the provider `MatchWithValueHandlers<ComputeArea>` performs the automatic variant dispatching of `ComputeArea` through `MatchWithHandlers`.
+
+Even though `Shape` only has two variant, a key strength of dispatchers like `MatchWithValueHandlers` is that it would scale to enums with arbitrary number of variants, with any kind of variant computation. For example, we can as easily implement `HasArea` for `ShapePlus` as follows:
+
+```rust
+impl HasArea for ShapePlus {
+    fn area(self) -> f64 {
+        MatchWithValueHandlers::<ComputeArea>::compute(&(), PhantomData::<()>, self)
+    }
+}
+```
+
+Although there are still a bit of boilerplate remains, the implementation is much simpler than having to manually match and dispatch the variants by hand, and more reliable than performing the same operation using macros. It is also possible that CGP will offer more ergonomic abstractions on the final usage, so that simple use cases like the implementation of `HasArea` can be further streamlined and simplified.
+
 # Conclusion

From d8c5181a64f9abf228a6909913a34f10f4ae5d6d Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Mon, 28 Jul 2025 19:34:29 +0000
Subject: [PATCH 06/21] AI-revise sections

---
 .../2025-07-20-extensible-datatypes-part-4.md | 206 +++++++++---------
 1 file changed, 103 insertions(+), 103 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index ea7342c..7f7d85f 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -71,7 +71,7 @@ This allows the `Shape` enum to be constructed generically using just the tag an
 
 ## Restrictions on Enum Shape
 
-To ensure ergonomics and consistency, CGP restricts the kinds of enums that can derive `FromVariant`. Specifically, supported enums must follow the **sums of products** pattern—each variant must contain *exactly one unnamed field*.
+To ensure ergonomics and consistency, CGP restricts the kinds of enums that can derive `FromVariant`. Specifically, supported enums must follow the **sums of products** pattern — each variant must contain *exactly one unnamed field*.
 
 The following forms, for example, are **not** supported:
 
@@ -155,7 +155,7 @@ The `Void` type is defined as an empty enum with no variants. This means that it
 
 Conceptually, `Void` serves the same purpose as Rust’s built-in [**never type**](https://doc.rust-lang.org/reference/types/never.html) or the [`Infallible`](https://doc.rust-lang.org/std/convert/enum.Infallible.html) type from the standard library. However, CGP defines `Void` explicitly to distinguish its special role in the context of extensible variants.
 
-While `IsNothing` is used for absent fields in partial records, we use `IsVoid` to represent removed or matched variants in partial enums. This ensures that once a variant has been extracted, it cannot be matched again — preserving both soundness and safety in CGP’s type-driven pattern matching.
+While `IsNothing` is used for absent fields in partial records, we use `IsVoid` to represent removed or matched variants in partial variants. This ensures that once a variant has been extracted, it cannot be matched again — preserving both soundness and safety in CGP’s type-driven pattern matching.
 
 ## `ExtractField` Trait
 
@@ -226,14 +226,13 @@ In this example, we begin by converting the `Shape` value into a `PartialShape`
 By the time we reach the second `Err` case, the remainder has the type `PartialShape<IsVoid, IsVoid>`, which cannot contain any valid variant. Because of this, we can safely omit any further pattern matching, and the compiler guarantees that there are no unreachable or unhandled cases.
 
 What makes this approach so powerful is that the Rust type system can statically verify that it is impossible to construct a valid value for `PartialShape<IsVoid, IsVoid>`. We no longer need to write boilerplate `_ => unreachable!()` code or use runtime assertions. The type system ensures exhaustiveness and soundness entirely at compile time, enabling safer and more maintainable implementation of extensible variants.
-
 ## Short Circuiting Remainder
 
-The earlier implementation of `compute_area` involves the use of nested `match` on the `Result` returned from `extract_field`. If you are familiar with the use of `?` on `Result`, you might wonder why don't we use it in the method implementation.
+In our earlier implementation of `compute_area`, we used nested `match` expressions to handle the `Result` returned from each call to `extract_field`. If you are familiar with the `?` operator in Rust, you might be wondering why we didn’t use it here to simplify the logic.
 
-The main reason `?` wouldn't work with our example is because we want to short circuit and return the `Ok` variant early, while the remainder type in the `Err` variant changes in each call to `extract_field`. This is the *inverse* of how we usually expect `Result` to be used, where the `Err` variant is returned early, while the type in the `Ok` variant changes with each method call.
+The reason is that we want to short circuit and return the `Ok` variant as soon as a match succeeds, while the `Err` case contains a remainder type that changes with each call to `extract_field`. This behavior is the inverse of how `Result` is typically used in Rust, where the `Err` variant is the one that gets returned early, and the `Ok` type changes as the computation progresses.
 
-To really simplify the code, we can imagine it to be written as the following pseudocode:
+To better understand what we are trying to achieve, consider the following pseudocode that illustrates the intent more clearly:
 
 ```rust
 pub fn compute_area(shape: Shape) -> Result<f64, Infallible> {
@@ -250,17 +249,17 @@ pub fn compute_area(shape: Shape) -> Result<f64, Infallible> {
 }
 ```
 
-In the above pseudocode, we *invent* a new operator `⸮`, which behaves in the opposite way as `?`. It short circuits and return the `Ok` variant early, or binds the remainder value in the `Err` variant to the `remainder` variable in the `let` binding.
+In this pseudocode, we introduce a fictional operator `⸮`, which behaves like the opposite of `?`. Instead of short circuiting on `Err`, it short circuits on `Ok`, returning the value immediately. If the result is `Err`, it binds the remainder to the `remainder` variable and continues.
 
-In the example, we use `.map` to map the `Ok` variant from the shape to the `f64` area result, and return early through `⸮`. Otherwise, the `remainder` variable gradually shrinks, until it becomes inhabitable in the end. We then use `match remainder {}` to statically assert that the case cannot be reach.
+In this setup, each call to `extract_field` uses `.map` to transform a successful match into the final `f64` result. If the match succeeds, `⸮` returns early. Otherwise, we continue with the remainder, which gradually becomes more constrained until it is fully uninhabited. Once all variants have been tried, the final `match remainder {}` statically asserts that no remaining case is possible.
 
-In other words, our new `compute_area` function don't really "return" anything in the `Err` variant. But to complete the type signature, we use `Result<f64, Infallible>` as the return type, with `Infallible` representing that the operation can never fail with an `Err` variant.
+This highlights a subtle but important point: the `compute_area` function never actually returns an `Err` in practice. To satisfy the function’s signature, we return a `Result<f64, Infallible>`, where `Infallible` indicates that failure is not possible.
 
-At this point, some readers may point out that there are other ways to simplify the Rust code without introducing the `⸮` operator. For example, we could make `extract_field` return `Result<Remainder, Value>` so that we can short circuit `Err(Value)` through `?`. However, doing so may be confusing, as it makes more sense that `Remainder` is the "exceptional" case here.
+Some readers may suggest alternative approaches, such as flipping the result to `Result<Remainder, Value>` so that the `?` operator could be used to return the value directly. While that might make the surface syntax cleaner, it reverses the intuitive meaning of the result. In this case, `Remainder` is the exceptional path, and `Value` is what we expect when the extraction succeeds.
 
-Furthermore, we introduce `⸮` as an exercise for you to think about how it relates to other existing operators such as `?`, `.await`, and `.await?`. In practice, we won't need Rust to officially support `⸮`, nor would we need to manually implement functions like `compute_area`. Instead, the extensible visitor pattern would apply something like `⸮` for us in the implementation to automatically implement `compute_area` for us.
+The introduction of `⸮` is not meant to advocate for a new language feature. Rather, it serves to clarify the control flow and encourage you to think about how this pattern relates to existing Rust constructs like `?`, `.await`, and combinations such as `.await?`. In practice, we do not need to manually write functions like `compute_area` or invent new operators. The extensible visitor pattern we will explore later provides a mechanism that effectively captures this logic for us.
 
-We will revisit this topic in the later section about the implementation of extensible visitor. For now, let's move on to how we finalize an empty remainder.
+We will revisit this idea when we discuss how the visitor pattern automates this process. For now, let’s continue by looking at how to finalize an empty remainder.
 
 ## `FinalizeExtract` Trait
 
@@ -456,7 +455,7 @@ First, the blanket implementation of `CanUpcast` verifies several conditions:
       Field<symbol!("Rectangle"), Rectangle>,
   ]
   ```
-* `Shape` must also implement `HasExtractor`, with its associated extractor being `PartialShape<IsPresent, IsPresent>`.
+* `Shape` must also implement `HasExtractor`, with its associated `Extractor` type being `PartialShape<IsPresent, IsPresent>`.
 * The `Fields` type must implement `FieldsExtractor`, with `PartialShape<IsPresent, IsPresent>` as the source and `ShapePlus` as the target.
 * The result of the extraction yields a remainder of type `PartialShape<IsVoid, IsVoid>`, which in turn implements `FinalizeExtract`.
 
@@ -465,7 +464,7 @@ Next, the `FieldsExtractor` implementation for the head of the sum begins proces
 * The current `Tag` is `symbol!("Circle")`, and the associated `Value` is of type `Circle`.
 * The `Source` is `PartialShape<IsPresent, IsPresent>`, and the `Target` is `ShapePlus`.
 * The source implements `ExtractField<symbol!("Circle")>`, which succeeds with `Circle` as the extracted value and `PartialShape<IsVoid, IsPresent>` as the remainder.
-* The target `ShapePlus` implements `FromVariant<symbol!("Circle")>`, again using the `Circle` type.
+* The target `ShapePlus` implements `FromVariant<symbol!("Circle")>`, again with `Circle` being the `Value` type.
 
 The extractor then proceeds to the next variant in the sum:
 
@@ -542,13 +541,15 @@ However, when the actual variant in this case is `Triangle`, which is not part o
 
 One of the most impressive aspects of both upcast and downcast is that they work seamlessly even when the source and target enums define their variants in entirely different orders. Because the trait implementations, such as `ExtractField`, operate in a generic and order-independent way, the correctness and behavior of casting are preserved regardless of variant ordering. This level of flexibility makes the CGP approach to extensible variants both powerful and practical for real-world use.
 
-# Visitor Dispatcher
+---
+
+# Implementation of Visitor Dispatcher
 
-With the traits for extensible variants defined, we can now turn our attention to how CGP implements generalized **visitor dispatchers**, similar to how the [builder dispatchers](/blog/extensible-datatypes-part-3/#builder-dispatcher) in our previous blog post was implemented.
+With the traits for extensible variants now in place, we can turn our attention to how CGP implements generalized **visitor dispatchers**, similar to the [builder dispatchers](/blog/extensible-datatypes-part-3/#builder-dispatcher) described in the previous part of this series.
 
-## `MatchWithHandlers` Provider
+## `MatchWithHandlers`
 
-In the [examples in part 2](/blog/extensible-datatypes-part-2/#dispatching-eval), we have used dispatchers like `MatchWithValueHandlers` and `MatchWithValueHandlersRef` to dispatch the handling of variant values to different handlers based on the `Input` type. Under the hood, these dispatchers are built upon a more fundamental dispatcher called `MatchWithHandlers`, which is implemented as follows:
+In the [examples from Part 2](/blog/extensible-datatypes-part-2/#dispatching-eval), we introduced dispatchers such as `MatchWithValueHandlers` and `MatchWithValueHandlersRef`, which delegate the handling of enum variants to different visitor handlers based on the `Input` type. These dispatchers are built on top of a more fundamental dispatcher called `MatchWithHandlers`, whose implementation is shown below:
 
 ```rust
 #[cgp_provider]
@@ -573,46 +574,49 @@ where
 }
 ```
 
-The `MatchWithHandlers` provider is parameterized by a `Handlers` type, which represents a type-level list of visitor handlers that will process the variants from a generic `Input` type. Inside the `Computer` implementation, it requires `Input` to be an enum that implements `HasExtractor`.
+The `MatchWithHandlers` provider is parameterized by a `Handlers` type, which represents a type-level list of visitor handlers responsible for processing the variants of a generic `Input` enum. The implementation requires `Input` to implement the `HasExtractor` trait, which provides access to its partial variants.
+
+Within the `compute` method, we first convert the input into its extractor form using `input.to_extractor()`. This partial variant is then passed to the lower-level dispatcher `DispatchMatchers<Handlers>`, which attempts to match and handle each variant. It returns a `Result<Output, Remainder>`, where a successful match produces an `Output`, and an unmatched remainder is returned otherwise.
 
-Inside the method body for `compute`, it first calls `input.to_extractor()` to convert the input into partial variants, and then pass it to a lower level `DispatchMatchers<Handlers>` handler, which performs the actual dispatching logic and returns `Result<Output, Remainder>` as the output. The implementation then expects the `Remainder` that is returned to be inhabitable through `FinalizeExtract`. With that, it simply match on the return result to return the `Output`, and in the error case, it uses `finalize_extract()` to assert that the case cannot be reached.
+If the result is `Ok`, the output is returned directly. If the result is `Err`, we call `finalize_extract()` on the remainder to assert that it is uninhabited, ensuring that all variants have been exhaustively handled. This makes the case both safe and unreachable, as guaranteed by the type system.
 
+### `DispatchMatchers`
 
-## `DispatchMatchers` Provider
+In our earlier implementation of extensible builders via `BuildWithHandlers`, we [used `PipeHandlers`](/blog/extensible-datatypes-part-3/#buildwithhandlers-provider) to compose a pipeline of builder handlers that successively filled in partial records. For extensible visitors, we follow a similar pattern with a slight variation that reflects the different control flow.
 
-Recall that in the extensible builder implementation of `BuildWithHandlers`, we used `PipeHandlers` to form a pipeline of builders to gradually fill in the partial records returned by the previous builder handler. For the case of extensible visitors, the pipeline implementation is slightly different, but is still elegantly defined as follows:
+The dispatcher `DispatchMatchers` is defined as follows:
 
 ```rust
 pub type DispatchMatchers<Providers> = PipeMonadic<OkMonadic, Providers>;
 ```
 
-What this means is that `DispatchMatchers` forms a **monadic** pipeline of visitor handlers, that is based on the monad implementation provided by `OkMonadic`.
+This definition constructs a **monadic pipeline** of visitor handlers, using `OkMonadic` as the monad implementation.
 
 ## What is a Monad?!
 
-At this point, most of the readers coming from Rust background would probably be confused on *what the heck is a [monad](https://wiki.haskell.org/Monad)*, and what does it have to do with implementing extensible visitors. So in this section, I will try to explain monads in much more simplified and less intimidating ways, using existing Rust concepts.
+At this point, many readers coming from a Rust background may be wondering what exactly a [monad](https://wiki.haskell.org/Monad) is, and how it relates to implementing extensible visitors. In this section, we will break down the concept in simplified terms using familiar Rust patterns and constructs.
 
-To put it simply, a monad `M` is typically a container types that "contains" some value type `T` in a "shape" that looks like `M<T>`. We have in fact commonly used various monadic types in Rust, including `Option<T>`, `Result<T, E>`, and `impl Futures<Output = T>`.
+A monad, often written as `M`, is a type that acts as a container for another value `T`, and it typically appears in the form `M<T>`. If you have worked with `Option<T>`, `Result<T, E>`, or asynchronous code using `impl Future<Output = T>`, then you have already used monadic types in Rust.
 
-In addition to "containing" values, a monad provide a "bind" operation to "extract" the values out of it. In Rust, we have also frequently used this operation through the use of `?`, `.await`, or `.await?`.
+Monads are not just containers. They also provide a way to operate on the values they contain, typically through an operation known as "bind." In Rust, this concept appears through the use of operators like `?`, `.await`, and `.await?`, all of which allow you to "extract" or "unwrap" the value inside a container and propagate control based on the result.
 
-Learning all these, we can now understand `PipeMonadic` as simply asking CGP to automatically "apply" operators like `?`, `.await`, or `.await?` to the previous handler in the pipeline, to "extract" the result before passing it as an input to the next handler.
+With this understanding, we can think of `PipeMonadic` in CGP as a mechanism that automatically applies these unwrapping operations between steps in a pipeline. It takes the result from one handler and, using a monadic operator, unwraps it before passing it along as input to the next handler. This is how CGP builds a pipeline of computations where each step can short-circuit or continue depending on its output.
 
-The main strength of the monadic implementation is that we can not only use existing operators like `?`, but also perform similar operation on other types that have similar properties. This includes a composition of the existing types that we are already familiar with, such as `impl Futures<Output = Result<Result<Option<T>, E1>, E2>>`. With monads, you can imagine that we can now implement an operator that is roughly equivalent to `.await???` to "extract" the `T` value out of the nested container.
+The real strength of this approach is that it generalizes well. You are not limited to a specific type like `Result`; you can apply the same logic to any monad-like type, including more complex combinations such as `impl Future<Output = Result<Result<Option<T>, E1>, E2>>`. In principle, you could imagine applying something like `.await???` to extract the inner value, and with monads, this can be abstracted and automated.
 
 ### `OkMonadic` Monad Provider
 
-For the case of `DispatchMatchers`, the "monad provider" we used is called `OkMonadic`. Essentially, this is the `⸮` operator that we have used in the pseudocode in the [`compute_area` example](#short-circuiting-remainder) to short circuit on the `Ok` variant and have a changing remainder type inside `Err`.
+In the case of `DispatchMatchers`, the monad provider we use is called `OkMonadic`. This corresponds to the custom operator `⸮` we introduced in the pseudocode in the [`compute_area` example](#short-circuiting-remainder), which short-circuits on the `Ok` variant and passes along the changing `Err` remainder.
 
-So what we are really saying here is that, to implement `DispatchMatchers`, we just need to form a pipeline of handlers, where we use `⸮` to early return an `Ok(output)` from the returned `Result<Output, Remainder>` from the previous handler. Otherwise, we get back `Remainder` from `⸮`, and feed it as the input to the next handler.
+When we say that `DispatchMatchers` is defined using `PipeMonadic<OkMonadic, Providers>`, we mean that CGP should build a handler pipeline where each step uses the `⸮` operator to either return early with `Ok(output)` or continue processing the `Err(remainder)` with the next handler.
 
-Thanks to `PipeMonadic` and `OkMonadic`, we do not need to implement this extraction manually. Instead, we just use the existing facilities provided by CGP to automatically implement this monadic pipeline for us.
+Because of `PipeMonadic` and `OkMonadic`, we do not need to write this logic ourselves. CGP handles the monadic control flow automatically, allowing us to focus on the behavior of each handler without worrying about wiring them together manually.
 
-If you are still confused at this point, don't worry, as we will walk through a concrete example next. We will also have a blog post in the near future to explain about how CGP implements monads in Rust to support operations like `PipeMonadic`.
+If any of this still feels unclear, do not worry. We will walk through a concrete example next to clarify how it works in practice. We also plan to publish a separate blog post that dives deeper into how CGP implements monads in Rust, including the internals of `PipeMonadic` and related abstractions.
 
 ## Example Use of `MatchWithHandlers`
 
-To see how we can use `MatchWithHandlers` directly, let's revisit the earlier example of computing the area of `Shape`. We first define two separate `Computer` providers to compute the `Circle` and `Rectangle` variants of shape as follows:
+To understand how to use `MatchWithHandlers` directly, let's revisit the example of computing the area of a `Shape`. We start by defining two separate `Computer` providers that calculate the area for the `Circle` and `Rectangle` variants:
 
 ```rust
 #[cgp_computer]
@@ -628,7 +632,7 @@ fn rectangle_area(rectangle: Rectangle) -> f64 {
 
 ### `#[cgp_computer]` Macro
 
-We use the `#[cgp_computer]` macro to turn our pure functions into context-generic providers that can be referenced as types. Behind the scene, `#[cgp_computer]` generates a `Computer` implementation that looks like:
+The `#[cgp_computer]` macro allows us to transform these pure functions into context-generic providers that can be referenced as types. Behind the scenes, this macro generates a `Computer` implementation similar to the following:
 
 ```rust
 #[cgp_provider]
@@ -641,11 +645,11 @@ impl<Context, Code> Computer<Context, Code, Circle> for CircleArea {
 }
 ```
 
-As we can see, `#[cgp_computer]` gives us the convenience that we can define pure `Computer` instances more easily, by just writing them as functions. Since we ignore that `Context` and `Code` types, we can instantiate the provider to work with any `Context` or `Code` that we provide to it.
+This macro simplifies the process of defining `Computer` instances by letting us write them as plain functions. Because the macro ignores the `Context` and `Code` types, the generated provider works with any `Context` and `Code` you supply.
 
 ### `ComputeShapeArea` Handler
 
-With `CircleArea` and `RectangleArea` defined, we can now define a `ComputeShapeArea` handler as a type alias that uses `MatchWithHandlers` as follows:
+With `CircleArea` and `RectangleArea` defined, we can create a `ComputeShapeArea` handler by using `MatchWithHandlers` as a type alias:
 
 ```rust
 pub type ComputeShapeArea = MatchWithHandlers::<
@@ -656,20 +660,20 @@ pub type ComputeShapeArea = MatchWithHandlers::<
 >;
 ```
 
-Instead of passing the providers to `MatchWithHandlers` directly, they are wrapped by some other constructs. First, the `ExtractFieldAndHandle` handler extracts the variant value from the specified tag, e.g. `symbol!("Circle")`, and pass it to the inner handler `HandleFieldValue<CircleArea>`.
+Rather than passing providers directly to `MatchWithHandlers`, we wrap them with helper handlers. The `ExtractFieldAndHandle` handler extracts the variant value associated with a specific tag, such as `symbol!("Circle")`, and forwards it to the inner handler `HandleFieldValue<CircleArea>`.
 
-Next, the inner input is given in the form `Field<symbol("Circle"), Circle>`, and the `HandleFieldValue` handler extracts the `Circle` value from `Field`, and passes it as the input to `CircleArea`. We will walk through the implementation of `ExtractFieldAndHandle` and `HandleFieldValue` right after this, but first, let's take a look at how `ComputeShapeArea` can be used.
+The inner handler `HandleFieldValue` receives the input as `Field<symbol!("Circle"), Circle>`, extracts the `Circle` value, and passes it to `CircleArea`. We will explore the implementations of `ExtractFieldAndHandle` and `HandleFieldValue` shortly, but first, let's see how `ComputeShapeArea` is used.
 
-As a whole, we expect the instantiated `MatchWithHandlers` implements the `Computer` trait, and we call `compute` on it with `()` as the `Context` type and also the `Code` type as follows:
+As a whole, the instantiated `MatchWithHandlers` implements the `Computer` trait. We can call `compute` on it using `()` for both the `Context` and `Code` types like this:
 
 ```rust
 let shape = Shape::Circle(Circle { radius: 5.0 });
 let area = ComputeShapeArea::compute(&(), PhantomData::<()>, shape);
 ```
 
-This works, because the `Computer` instances that we have defined earlier with `#[cgp_computer]` could work with any `Context` and `Code` type.
+This works because the `Computer` instances defined with `#[cgp_computer]` are generic over any `Context` and `Code`.
 
-Behind the scene, `MatchWithHandlers` implements `ComputeShapeArea` roughly as something like the following pseudocode:
+Under the hood, `MatchWithHandlers` implements `ComputeShapeArea` roughly as the following pseudocode:
 
 ```rust
 let remainder = shape.to_extractor();
@@ -680,18 +684,18 @@ let remainder = remainder
 
 let remainder = remainder
     .extract_field(symbol!("Rectangle"))
-    .map(|rectangle| Rectangle::compute(&(), PhantomData::<()>, rectangle))⸮;
+    .map(|rectangle| RectangleArea::compute(&(), PhantomData::<()>, rectangle))⸮;
 
 remainder.finalize_extract();
 ```
 
-That is, `MatchwithHandlers` now performs the same `⸮` [short circuit operation](#short-circuiting-remainder) that we hand-implemented earlier. And for each mapping of the `Ok` result in the extraction, we instead use the `Computer` providers to perform the area calculations.
+Here, `MatchWithHandlers` performs the same `⸮` short-circuit operation described earlier in the [short-circuiting remainder](#short-circuiting-remainder) section. For each `Ok` value returned by extraction, the corresponding `Computer` provider computes the area.
 
-From the expansion, we can see the amount of boilerplate code that `MatchWithHandlers` has implemented for us. This in turn is implemented simply as a monadic pipeline through `PipeMonadic`, with `OkMonadic` being "passed" as the `⸮` operator that we have used in this pseudocode expansion.
+This example highlights how much boilerplate `MatchWithHandlers` abstracts away for us. Its implementation is essentially a monadic pipeline built using `PipeMonadic`, where `OkMonadic` provides the behavior of the `⸮` operator used in this pseudocode.
 
 ## `ExtractFieldAndHandle`
 
-To understand better how the `MatchWithHandlers` example earlier works, we will take a look at how the `ExtractFieldAndHandle` provider is implemented:
+To better understand how the earlier `MatchWithHandlers` example works, let's examine the implementation of the `ExtractFieldAndHandle` provider:
 
 ```rust
 #[cgp_provider]
@@ -716,9 +720,9 @@ where
 }
 ```
 
-The type signature for `ExtractFieldAndHandle` may look complicated, but what is does is pretty simple. Given some partial variants `Input`, the handler tries to use `ExtractField` to extract a variant with the specified `Tag` from the partial variants. If the variant extraction is successful, it feeds the tagged field value `Field<Tag, Value>` to the inner `Provider` to process the variant. Otherwise, it returns the remainder as an `Err`, so that it will be handled by the next handler in the monadic pipeline.
+While the type signature may seem complex, the behavior is straightforward. Given some partial variants `Input`, this handler attempts to extract a variant with the specified `Tag` using `ExtractField`. If extraction succeeds, it wraps the extracted variant as a tagged field `Field<Tag, Value>` and passes it to the inner `Provider` for processing. If extraction fails, it returns the remainder as an `Err`, allowing the next handler in the monadic pipeline to try.
 
-It is worth noting that we pass a tagged `Field<Tag, Value>` to the inner `Provider`, instead of a bare `Value`, so that the provider is given an option to handle multiple variants with the same `Value` type based on the variant `Tag`. As an example, consider the following enum:
+Note that the inner `Provider` receives a tagged `Field<Tag, Value>` rather than a bare `Value`. This allows the provider to differentiate variants that share the same `Value` type but differ in their variant `Tag`. For example, consider:
 
 ```rust
 pub enum FooBar {
@@ -727,11 +731,11 @@ pub enum FooBar {
 }
 ```
 
-Even though both the `Foo` and `Bar` variants have `u64` as the `Value` type, they will be passed from `ExtractFieldAndHandle` as `Field<symbol!("Foo"), u64>` and `Field<symbol!("Bar"), u64>` to the inner provider. This means that the inner provider may choose to handle the `Foo` and `Bar` variants differently, by matching on the field `Tag`.
+Here, both `Foo` and `Bar` hold `u64` values. `ExtractFieldAndHandle` will pass these as `Field<symbol!("Foo"), u64>` and `Field<symbol!("Bar"), u64>` respectively, so the provider can handle them differently by matching on the `Tag`.
 
 ### `HandleFieldValue`
 
-The tagged `Field` input from `ExtractFieldAndHandle` is useful for cases when there are multiple variants with the same `Value` type. However, in simpler cases such as `Shape`, we often don't care about the variant `Tag` and would like to process the value directly. To simplify the process, we can use the `HandleFieldValue` wrapper to "peel" off the `Field` wrapper from the `Input`, and get the `Value` directly as an input to the inner provider. The `HandleFieldValue` is implemented as follows:
+The tagged `Field` input from `ExtractFieldAndHandle` is useful when multiple variants share the same `Value` type. However, in simpler cases like our `Shape` example, we often just want to handle the contained value directly, ignoring the tag. The `HandleFieldValue` wrapper simplifies this by “peeling off” the `Field` wrapper and passing only the inner value to the inner provider:
 
 ```rust
 #[cgp_provider]
@@ -752,11 +756,11 @@ where
 }
 ```
 
-As we can see, `HandleFieldValue` simply works with any input type in the form `Field<Tag, Input>`, unwrap the input value from `Field`, and then pass it to the inner provider.
+As shown, `HandleFieldValue` simply unwraps the input from `Field<Tag, Input>` and forwards the contained `Input` value to the inner provider.
 
 ### Revisiting `ComputeShapeArea`
 
-Now that we have understood how `ExtractFieldAndHandle` and `HandleFieldValue` are implemented, let's walk through again what really happens when we use `MatchWithHandlers` in `ComputeShapeArea`:
+Now that we've understood `ExtractFieldAndHandle` and `HandleFieldValue`, let’s review what happens inside `ComputeShapeArea`:
 
 ```rust
 pub type ComputeShapeArea = MatchWithHandlers::<
@@ -767,23 +771,22 @@ pub type ComputeShapeArea = MatchWithHandlers::<
 >;
 ```
 
-- `MatchWithHandlers` uses `HasExtractor` to convert `Shape` into `PartialShape<IsPresent, IsPresent>`, and passes it to `ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<CircleArea>>`.
-- `ExtractFieldAndHandle` uses `ExtractField<symbol("Circle")>` to extract the `Circle` variant from `PartialShape<IsPresent, IsPresent>`.
-    - If successful, the value is passed as `Field<symbol("Circle"), Circle>` to `HandleFieldValue<CircleArea>`.
-        - `HandleFieldValue<CircleArea>` extracts the `Circle` value from `Field<symbol("Circle"), Circle>`, and passes it to `CircleArea`.
-    - Otherwise, the remainder `PartialShape<IsVoid, IsPresent>` is returned as error.
-- `ExtractFieldAndHandle` uses `ExtractField<symbol("Rectangle")>` to extract the `Rectangle` variant from `PartialShape<IsVoid, IsPresent>`.
-    - If successful, the value is passed as `Field<symbol("Rectangle"), Rectangle>` to `HandleFieldValue<RectangleArea>`.
-        - `HandleFieldValue<RectangleArea>` extracts the `Circle` value from `Field<symbol("Rectangle"), Rectangle>`, and passes it to `RectangleArea`.
-    - Otherwise, the remainder `PartialShape<IsVoid, IsVoid>` is returned as error.
-- `MatchWithHandlers` uses `FinalizeExtract` on `PartialShape<IsVoid, IsVoid>` to discharge the impossible remainder.
-
+* `MatchWithHandlers` uses `HasExtractor` to convert `Shape` into `PartialShape<IsPresent, IsPresent>`, then passes it to `ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<CircleArea>>`.
+* `ExtractFieldAndHandle` attempts to extract the `Circle` variant from `PartialShape<IsPresent, IsPresent>`:
+  * If successful, the extracted value is passed as `Field<symbol!("Circle"), Circle>` to `HandleFieldValue<CircleArea>`.
+    * `HandleFieldValue<CircleArea>` unwraps the `Circle` value and passes it to `CircleArea`.
+  * Otherwise, the remainder `PartialShape<IsVoid, IsPresent>` is returned as an error.
+* Next, `ExtractFieldAndHandle` tries to extract the `Rectangle` variant from `PartialShape<IsVoid, IsPresent>`:
+  * If successful, the extracted value is passed as `Field<symbol!("Rectangle"), Rectangle>` to `HandleFieldValue<RectangleArea>`.
+    * `HandleFieldValue<RectangleArea>` unwraps the `Rectangle` and passes it to `RectangleArea`.
+  * Otherwise, the remainder `PartialShape<IsVoid, IsVoid>` is returned as an error.
+* Finally, `MatchWithHandlers` calls `FinalizeExtract` on `PartialShape<IsVoid, IsVoid>` to assert that the remainder is empty and discharge the impossible case.
 
 ## Unifying Variant Value Handlers
 
-At this point, we have seen how `MatchWithHandlers` can be used as a low-level construct for implementing extensible visitors. However, `MatchWithHandlers` requires the handler of each variant to be explicitly provided in the list of handlers given to it. We will now look at how higher-level handlers like `MatchWithValueHandlers` can be implemented to automatically derive the list of variant handlers to be passed to `MatchWithHandlers`.
+So far, we have seen how `MatchWithHandlers` can serve as a powerful low-level tool to implement extensible visitors. However, it requires explicitly listing a handler for each variant in the provided handler list. To make this process more ergonomic, we can build higher-level abstractions like `MatchWithValueHandlers`, which automatically derives the list of variant handlers passed to `MatchWithHandlers`.
 
-But before we can implement `MatchWithValueHandlers`, we first need to slightly modify the variant handler for all entries in `MatchWithHandlers`, such that the same handler is used for all variants:
+Before implementing `MatchWithValueHandlers`, we first need to unify the variant handlers used in `MatchWithHandlers`. Instead of specifying separate handlers for each variant, we modify the variant handlers so that the same handler is used for all variants. For example:
 
 ```rust
 pub type ComputeShapeArea = MatchWithHandlers<
@@ -794,9 +797,9 @@ pub type ComputeShapeArea = MatchWithHandlers<
 >;
 ```
 
-In the updated definition, we use `ComputeArea` instead of `CircleArea` and `RectangleArea` to compute the area for *both* `Circle` and `Rectangle`. By doing so, we can see that `ComputeShapeArea` now has a unified pattern of `ExtractFieldAndHandle<Tag, HandleFieldValue<ComputeArea>>` for each of its entry. And we can now build further abstraction to simplify the repeating patterns.
+Here, rather than using distinct `CircleArea` and `RectangleArea` handlers, we use a single handler, `ComputeArea`, for both variants. This creates a unified pattern of `ExtractFieldAndHandle<Tag, HandleFieldValue<ComputeArea>>` for each entry. Recognizing this repetition allows us to build further abstractions that simplify these common patterns.
 
-But before we do that, let's look at how `ComputeArea` can be implemented. For the case of `Shape`, as well of for many use cases of extensible variants, we can just define a regular Rust trait for computing the area of each shape variant:
+To understand this better, let's explore how `ComputeArea` itself can be implemented. For many extensible variants such as `Shape`, a straightforward approach is to define a regular Rust trait that computes the area for each variant:
 
 ```rust
 pub trait HasArea {
@@ -816,9 +819,9 @@ impl HasArea for Rectangle {
 }
 ```
 
-The definition and implementation of `HasArea` is pretty straightforward, with it providing an `area` method to compute the area for each shape variant. Crucially, we do *not* manually implement `HasArea` for `Shape`, as the point of extensible variants is to allow us to skip the boilerplate required to manually call the `area` method for each of the variant in `Shape`.
+This `HasArea` trait is simple and intuitive. Each variant implements the `area` method in the usual Rust way. Notice that we do not hand-implement `HasArea` for the overall `Shape` type, we will do this later on, by using `MatchWithValueHandlers` to help us perform the dispatching.
 
-Although `HasArea` is a vanilla Rust trait, we can pretty easily implement a `Computer` provider for it as follows:
+Although `HasArea` is a plain Rust trait, it is easy to wrap it as a `Computer` provider using the `#[cgp_computer]` macro:
 
 ```rust
 #[cgp_computer]
@@ -827,11 +830,11 @@ fn compute_area<T: HasArea>(shape: T) -> f64 {
 }
 ```
 
-Here, we use `#[cgp_computer]` on a `compute_area` function that is generic over any type that implements `HasArea`, and the implementation simply calls the `area` method. With this wrapper function, we now also have `ComputeArea` generated, which can be used in `ComputeShapeArea`.
+This generic function works for any type implementing `HasArea` and simply calls the `area` method. Applying `#[cgp_computer]` here generates the `ComputeArea` provider type that can then be used within `ComputeShapeArea`.
 
 ### `ToFieldsHandler`
 
-To simplify the implementation of `ComputeShapeArea`, we first need to figure a way to automatically generate the extractors given to `MatchWithHandlers`, which is:
+To simplify `ComputeShapeArea` further, we need a way to automatically generate the list of extractors passed to `MatchWithHandlers`. Concretely, we want to generate this:
 
 ```rust
 Product![
@@ -840,7 +843,7 @@ Product![
 ]
 ```
 
-Recall that `Shape` also implements [`HasFields`](#hasfields-implementation), which is in the form:
+Recall that `Shape` implements the `HasFields` trait, which exposes its variants as a type-level sum:
 
 ```rust
 Sum![
@@ -849,7 +852,7 @@ Sum![
 ]
 ```
 
-This means that all we have to do is to extract the tags from `Shape::Fields`, and replace each entry with `ExtractFieldAndHandle`. We can perform this transformation at the type level, by using a helper trait `ToFieldHandlers` that is defined as follows:
+This means we can programmatically extract the tags from `Shape::Fields` and replace each variant with an `ExtractFieldAndHandle` wrapper. We can perform this transformation entirely at the type level using a helper trait, `ToFieldHandlers`, defined as:
 
 ```rust
 pub trait ToFieldHandlers<Provider> {
@@ -869,9 +872,9 @@ impl<Provider> ToFieldHandlers<Provider> for Void {
 }
 ```
 
-At a high level, `ToFieldHandlers` works by iterating through each entry in a type-level sum, replace each entry that is in the shape `Field<Tag, Value>` with `ExtractFieldAndHandle<Tag, Provider>`, and convert the result to a type-level list.
+In essence, `ToFieldHandlers` recursively walks through each entry in a type-level sum, replacing `Field<Tag, Value>` with `ExtractFieldAndHandle<Tag, Provider>`, and converts the entire structure into a type-level list.
 
-Using `ToFieldHandlers`, we can now generate the arguments to `MatchWithHandlers` as something like follows:
+Using `ToFieldHandlers`, we can now write the `ComputeShapeArea` type as:
 
 ```rust
 pub type ComputeShapeArea = MatchWithHandlers<
@@ -881,11 +884,11 @@ pub type ComputeShapeArea = MatchWithHandlers<
 >;
 ```
 
-Although this version of `ComputeShapeArea` may look confusing, we won't be writing something like this in practice. Instead, the example definition is to demonstrate the transformation that is happening behind the scene to automatically generate the list of variant handlers from `Shape` to be given to `MatchWithHandlers`.
+This definition may look complex at first glance. However, it demonstrates the powerful behind-the-scenes transformation that automatically generates the list of variant handlers from `Shape` to be passed to `MatchWithHandlers`.
 
 ### `HasFieldHandlers`
 
-Using `ToFieldHandlers`, there is a two-step process to generate the variant handlers from `Shape` - first, we need to get `Shape`'s fields from `HasFields`, and then we need to apply `ToFieldHandlers` to the `Fields` type. We can further simplify the use through another helper trait called `HasFieldHandlers`, which is defined as follows:
+The process to generate variant handlers from `Shape` involves two steps: obtaining `Shape`’s fields from `HasFields`, and then applying `ToFieldHandlers` to those fields. To streamline this, we define another helper trait, `HasFieldHandlers`, that combines these steps:
 
 ```rust
 pub trait HasFieldHandlers<Provider> {
@@ -901,7 +904,7 @@ where
 }
 ```
 
-Essentially, `HasFieldHandlers` combines both the constraints for `HasFields` and `ToFieldHandlers`, so that we can use it as one non-nested constraint definition:
+`HasFieldHandlers` unifies the requirements of `HasFields` and `ToFieldHandlers` into a single, convenient trait. This lets us simplify the definition of `ComputeShapeArea` even further:
 
 ```rust
 pub type ComputeShapeArea = MatchWithHandlers<
@@ -909,17 +912,16 @@ pub type ComputeShapeArea = MatchWithHandlers<
 >;
 ```
 
-Using `HasFieldHandlers`, the definition of `ComputeShapeArea` becomes significantly simplified. We just need to use `HasFieldHandlers` to generate the variant handlers for `Shape`, and then pass that to `MatchWithHandlers`.
-
-In fact, if we look at the definition more carefully, we can apply the same pattern to be used on any `Input` type other than `Shape`, and any `Computer` provider other than `ComputeArea`.
+With `HasFieldHandlers`, the definition of `ComputeShapeArea` becomes much more concise. Instead of manually combining `HasFields` and `ToFieldHandlers`, we simply rely on `HasFieldHandlers` to generate them from `Shape` and pass the result to `MatchWithHandlers`.
 
+More importantly, this pattern is entirely general: it can be applied to any input type that implements `HasFields`, not just `Shape`, and to any `Computer` provider, not just `ComputeArea`.
 
 ## `MatchWithValueHandlers`
 
 The traits `HasFieldHandlers` and `ToFieldHandlers` serve as the helpers for us to perform *type-level metaprogramming* for us to implement high-level visitor dispatchers such as `MatchWithValueHandlers`, which is defined as follows:
 
 ```rust
-pub type MatchWithValueHandlers<Provider = UseContext> =
+pub type MatchWithValueHandlers<Provider> =
     UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<Provider>>>;
 
 delegate_components! {
@@ -930,39 +932,37 @@ delegate_components! {
 }
 ```
 
-The way we implement `MatchWithValueHandlers` is similar to how we implemented the builder dispatchers using [type-level metaprogramming in part 3](/blog/extensible-datatypes-part-3/#type-level-cgp-metaprogramming). The way it works is as follows: first, `MatchWithValueHandlers` is parameterized by a `Provider` that is expected to implement `Computer`, such as `ComputeArea`. The `Provider` parameter has a default `UseContext` value, which uses `CanCompute` from the context to perform the computation if no explicit `Computer` is provided.
-
-The implementation of `MatchWithValueHandlers` is simply a type alias to use `UseInputDelegate` to dispatch the `Input` type given in `Computer` to `MatchWithFieldHandlersInputs`, which contains a *generic* type-level map from `Input` to `MatchWithHandlers`.
+The design of `MatchWithValueHandlers` is similar to how we implemented the builder dispatchers using [type-level metaprogramming in part 3](/blog/extensible-datatypes-part-3/#type-level-cgp-metaprogramming). In this case, `MatchWithValueHandlers` is parameterized by a `Provider` that is expected to implement `Computer`, such as `ComputeArea`. The implementation of `MatchWithValueHandlers` is simply a type alias to use `UseInputDelegate` to dispatch the `Input` type given through `Computer` to `MatchWithFieldHandlersInputs`.
 
-The implementation of `MatchWithFieldHandlersInputs` is defined through `delegate_components!`, with it having a generic entry for any `Input` that implements `HasFieldHandlers<Provider>`. It then simply delegates the provider for that input to `MatchWithHandlers<Input::Handlers>`.
+The implementation of `MatchWithFieldHandlersInputs` is defined through `delegate_components!`, with it having a generic mapping for any `Input` that implements `HasFieldHandlers<Provider>`. It then simply delegates the provider for that input to `MatchWithHandlers<Input::Handlers>`.
 
 ### Example Instantiation of `MatchWithValueHandlers`
 
-Due to the advanced use of type-level metaprogramming, it may be challenging to understand how `MatchWithValueHandlers` and `MatchWithFieldHandlersInputs` works, especially when you read it for the first time. We will try to ease the understanding by looking at how this is used with `Shape`. First, the `ComputeShapeArea` definition can now be simplified to:
+Because `MatchWithValueHandlers` and `MatchWithFieldHandlersInputs` rely on type-level metaprogramming, it can be difficult to grasp exactly how they work on first encounter. To make things more concrete, let’s walk through how this abstraction is applied to `Shape`. With the machinery we’ve built, the definition of `ComputeShapeArea` becomes as simple as:
 
 ```rust
 pub type ComputeShapeArea = MatchWithValueHandlers<ComputeArea>;
 ```
 
-As we can see, `ComputeShapeArea` becomes simply a use of `MatchWithValueHandlers` with `ComputeArea`. The definition does not even include any reference of `Shape`, because it can now work with *any* compatible input type, including `Shape` and `ShapePlus`.
+This version of `ComputeShapeArea` is remarkably concise. It no longer mentions `Shape` directly, because it works with *any* compatible input type, including both `Shape` and extensions like `ShapePlus`.
 
-Behind the scene, the implementation of `ComputeShapeArea` is now redirected to `MatchWithHandlers` with the following steps:
+Under the hood, this type alias resolves to `MatchWithHandlers` through the following steps:
 
-- The type alias `MatchWithValueHandlers<ComputeArea>` is expanded to `UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>>`.
-- When the `Input` type is `Shape`, `UseInputDeleate` looks up the `DelegateComponent` entry for `Shape` in `MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>`.
-- `MatchWithFieldHandlersInputs` contains an entry for `Shape`, given that `Shape` implements `HasFieldHandlers<HandleFieldValue<ComputeArea>>`.
-- As demonstrated earlier, the `HasFieldHandlers` implementation for `Shape` expands to:
-    ```rust
-    Product![
-        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
-        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
-    ]
-    ```
-- This expanded type is now passed to `MatchWithHandlers`, which implements the visitor dispatch as we have walked through in earlier sections.
+* `MatchWithValueHandlers<ComputeArea>` expands to `UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>>`.
+* When the `Input` is `Shape`, `UseInputDelegate` locates the corresponding `DelegateComponent` mapping for `Shape` in `MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>`.
+* That mapping exists because `Shape` implements `HasFieldHandlers<HandleFieldValue<ComputeArea>>`.
+* As we saw earlier, this expands to:
+  ```rust
+  Product![
+      ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
+      ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<ComputeArea>>,
+  ]
+  ```
+* This type-level list is then passed to `MatchWithHandlers`, which performs the variant dispatch using the logic we’ve already explored.
 
 ## Implementing `HasArea` for `Shape`
 
-Using `MatchWithValueHandlers`, it becomes trivial to implement `HasArea` for `Shape` as follows:
+With `MatchWithValueHandlers`, implementing the `HasArea` trait for `Shape` becomes straightforward:
 
 ```rust
 impl HasArea for Shape {
@@ -972,9 +972,9 @@ impl HasArea for Shape {
 }
 ```
 
-Just as with the earlier example, since `ComputeArea` is generic over any `Context` and `Code` type, we use `()` for both the arguments in `compute`. Aside from the boilerplate, the provider `MatchWithValueHandlers<ComputeArea>` performs the automatic variant dispatching of `ComputeArea` through `MatchWithHandlers`.
+As in earlier examples, we use `()` for both the context and code parameters, since `ComputeArea` is generic over any `Context` and `Code`. Aside from this boilerplate, `MatchWithValueHandlers<ComputeArea>` takes care of automatically dispatching to the correct variant handler through `MatchWithHandlers`.
 
-Even though `Shape` only has two variant, a key strength of dispatchers like `MatchWithValueHandlers` is that it would scale to enums with arbitrary number of variants, with any kind of variant computation. For example, we can as easily implement `HasArea` for `ShapePlus` as follows:
+While `Shape` only has two variants, one of the major advantages of `MatchWithValueHandlers` is that it scales effortlessly to enums with many variants and more complex computations. For example, implementing `HasArea` for `ShapePlus` is just as simple:
 
 ```rust
 impl HasArea for ShapePlus {
@@ -984,6 +984,6 @@ impl HasArea for ShapePlus {
 }
 ```
 
-Although there are still a bit of boilerplate remains, the implementation is much simpler than having to manually match and dispatch the variants by hand, and more reliable than performing the same operation using macros. It is also possible that CGP will offer more ergonomic abstractions on the final usage, so that simple use cases like the implementation of `HasArea` can be further streamlined and simplified.
+Although some boilerplate still remains, this approach is significantly simpler than manually matching each variant or relying on procedural macros. It also brings more flexibility and type safety. In the future, CGP may provide more ergonomic layers on top of this pattern, making common use cases like `HasArea` even easier to express.
 
 # Conclusion

From 138150fa518e04529b8e44f9a29d015b32cc8345 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Mon, 28 Jul 2025 20:21:43 +0000
Subject: [PATCH 07/21] Write reference-based area computation

---
 .../2025-07-20-extensible-datatypes-part-4.md | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 7f7d85f..530d689 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -986,4 +986,66 @@ impl HasArea for ShapePlus {
 
 Although some boilerplate still remains, this approach is significantly simpler than manually matching each variant or relying on procedural macros. It also brings more flexibility and type safety. In the future, CGP may provide more ergonomic layers on top of this pattern, making common use cases like `HasArea` even easier to express.
 
+---
+
+# Visitor Dispatcher By Reference
+
+In the earlier examples, some dilligent readers may notice a serious flaw in the function signatures for the area computation of shapes, such as `HasArea::area` - the methods require *owned* values of the shape variants, rather than just reference to them. This means that a shape value is *consumed* every time we try to calculate its area, which is not exactly efficient!
+
+However, the reason we demonstrate the owned version of visitor dispatcher first is because they are simpler to understand, especially since we don't need to worry about dealing with lifetimes. Furthermore, we will demonstrate in this section that the reference-based visitor dispatcher can be implemented *on top* of the existing ownership-based visitor dispatcher. In other words, the reference-based implementation is simply a *special case* of the ownership-based implementation.
+
+In this section, we will walk through the detailed implementation of the reference-based visitor dispatcher. After this, you will hopefully appreciate how Rust's type system allows us to easily and safely extend the ownership-based visitor dispatcher to support references, all while making sure that there is no violation of lifetime constraints.
+
+## Reference-Based Area Computation
+
+To demonstrate how reference-based visitor dispatching would work, let's define a new `HasAreaRef` trait that calculates the area with a `&self` reference:
+
+```rust
+pub trait HasAreaRef {
+    fn area(&self) -> f64;
+}
+
+impl HasAreaRef for Circle {
+    fn area(&self) -> f64 {
+        PI * self.radius * self.radius
+    }
+}
+
+impl HasAreaRef for Rectangle { ... }
+impl HasAreaRef for Triangle { ... }
+```
+
+In practice, there is probably no use to keep both `HasArea` and `HasAreaRef`. But to avoid confusion, we will name this version differently so that it is clear which version of area computation we are using.
+
+Similarly, we use `#[cgp_computer]` to define a proxy `ComputeAreaRef` provider that implements `ComputerRef` using `HasAreaRef`:
+
+```rust
+#[cgp_computer]
+fn compute_area_ref<T: HasAreaRef>(shape: &T) -> f64 {
+    shape.area()
+}
+```
+
+With the reference-based area implementations in place, we can now implement `HasAreaRef` for `Shape` by simply using `MatchWithValueHandlersRef` instead of `MatchWithValueHandlers`:
+
+```rust
+impl HasAreaRef for Shape {
+    fn area(&self) -> f64 {
+        MatchWithValueHandlersRef::<ComputeAreaRef>::compute_ref(&(), PhantomData::<()>, self)
+    }
+}
+```
+
+Similarly, we can implement `HasAreaRef` for `ShapePlus` as easily with the same pattern:
+
+```rust
+impl HasAreaRef for ShapePlus {
+    fn area(&self) -> f64 {
+        MatchWithValueHandlersRef::<ComputeAreaRef>::compute_ref(&(), PhantomData::<()>, self)
+    }
+}
+```
+
+With the example, it may seem trivial to support reference-based visitor dispatch using `MatchWithValueHandlersRef`. In fact, as we will see next, this is indeed mostly true, aside from some additional complexities in handling generic lifetimes.
+
 # Conclusion

From 49cc34928eb7cb0277763a7e2ec4b7c00c924dda Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Mon, 28 Jul 2025 21:56:17 +0000
Subject: [PATCH 08/21] Draft partial-ref variants

---
 .../2025-07-20-extensible-datatypes-part-4.md | 77 +++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 530d689..8a3f7f1 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -1048,4 +1048,81 @@ impl HasAreaRef for ShapePlus {
 
 With the example, it may seem trivial to support reference-based visitor dispatch using `MatchWithValueHandlersRef`. In fact, as we will see next, this is indeed mostly true, aside from some additional complexities in handling generic lifetimes.
 
+## PartialRef Variants
+
+While most of the higher level implementations of reference-based extensible visitor is straightforward, we first need to also derive reference-based partial variants inside of `#[derive(ExtractField)]`. For instance, the following reference-based partial variants for `Shape` will be generated:
+
+```rust
+pub enum PartialRefShape<'a, F0: MapType, F1: MapType> {
+    Circle(F0::Map<&'a Circle>),
+    Rectangle(F0::Map<&'a Rectangle>),
+}
+```
+
+Compared to `PartialShape`, the definition of `PartialRefShape` contains an additional lifetime parameter `'a`, with each of its partial fields containing a `&'a` reference to the field value.
+
+The reason we need to define `PartialRefShape` in addition to `PartialShape` is because there is currently no easy way to express that a Rust value may "optionally" be a reference depending on the generic condition. As a result, it is more straightforward to have a separate enum that is parameterized by a lifetime. Furthermore, since the type is generated from macros and only used internally, this should not introduce too much overhead to the final implementation.
+
+### `HasExtractorRef` Trait
+
+Aside from the partial-ref variants, we also need a new `HasExtractorRef` trait to extract from a reference to the full enum:
+
+```rust
+pub trait HasExtractorRef {
+    type ExtractorRef<'a>
+    where
+        Self: 'a;
+
+    fn extractor_ref<'a>(&'a self) -> Self::ExtractorRef<'a>;
+}
+```
+
+Compared to `HasExtractor`, `HasExtractorRef` has a generic associated type `ExtractorRef` that is parameterized by a lifetime `'a`, and with an additional constraint that `Self: 'a`. It also provides an `extractor_ref` method that takes a `&'a self` reference, and returns the partial-ref variants as `ExtractorRef<'a>`.
+
+Aside from the additional lifetime, the `HasExtractorRef` implementation for `Shape` is rather straightforwardly implemented as follows:
+
+```rust
+impl HasExtractorRef for Shape {
+    type ExtractorRef<'a> = PartialRefShape<'a, IsPresent, IsPresent>
+    where Self: 'a;
+
+    fn extractor_ref<'a>(&'a self) -> Self::ExtractorRef<'a> {
+        match self {
+            Self::Circle(value) => PartialRefShape::Circle(value),
+            Self::Rectangle(value) => PartialRefShape::Rectangle(value),
+        }
+    }
+}
+```
+
+Using `extractor_ref`, we can now extract from a borrowed `Shape`, without needing to perform cloning on each of its variant.
+
+### `ExtractField` Implementation
+
+Fortunately, aside from the partial-ref variants and the `HasExtractorRef` trait, we can mostly reuse all other traits and use them as if we are interacting with owned values. This is because `PartialRefShape` technically contains "owned" variant values that are in the form `&'a Circle`, `&'a Rectangle`, etc. For instance, we can implement `ExtractField` for `PartialRefShape` as follows:
+
+```rust
+impl<'a, F1: MapType> ExtractField<symbol!("Circle")> for PartialRefShape<'a, IsPresent, F1> {
+    type Value = &'a Circle;
+    type Remainder = PartialShape<'a, IsVoid, F1>;
+
+
+    fn extract_field(
+        self,
+        _tag: PhantomData<symbol!("Circle")>,
+    ) -> Result<Self::Value, Self::Remainder> {
+        match self {
+            PartialRefShape::Circle(value) => Ok(value),
+            PartialRefShape::Rectangle(value) => Err(PartialRefShape::Rectangle(value)),
+        }
+    }
+}
+```
+
+The reason we can reuse traits like `ExtractField` is because the associated types like `Value` do not necessarily need to be the actual value from the original trait, and can also be a *reference* to the original value. So with this, we are pretending that the extensible variants contain reference for all its fields, and then manipulate them the same way as owned values.
+
+## `PromoteRef`
+
+
+
 # Conclusion

From 725fb7e39640dd384ac9f75b3accbc4a6fd6af33 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Mon, 28 Jul 2025 22:25:33 +0000
Subject: [PATCH 09/21] AI-revise sections

---
 .../2025-07-20-extensible-datatypes-part-4.md | 45 ++++++++++---------
 1 file changed, 23 insertions(+), 22 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 8a3f7f1..c6e01a4 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -988,17 +988,17 @@ Although some boilerplate still remains, this approach is significantly simpler
 
 ---
 
-# Visitor Dispatcher By Reference
+# Visitor Dispatcher by Reference
 
-In the earlier examples, some dilligent readers may notice a serious flaw in the function signatures for the area computation of shapes, such as `HasArea::area` - the methods require *owned* values of the shape variants, rather than just reference to them. This means that a shape value is *consumed* every time we try to calculate its area, which is not exactly efficient!
+In the earlier examples, some careful readers may have noticed a significant flaw in the function signatures for computing the area of shapes, such as in `HasArea::area`. These methods require *owned* values of the shape variants, meaning that each time we compute the area, we must consume the shape entirely. This is not ideal, especially when we only need a reference and want to preserve the original value.
 
-However, the reason we demonstrate the owned version of visitor dispatcher first is because they are simpler to understand, especially since we don't need to worry about dealing with lifetimes. Furthermore, we will demonstrate in this section that the reference-based visitor dispatcher can be implemented *on top* of the existing ownership-based visitor dispatcher. In other words, the reference-based implementation is simply a *special case* of the ownership-based implementation.
+We started with the ownership-based visitor dispatcher because it is conceptually simpler. It avoids the need to reason about lifetimes, making it easier to understand the overall implementation of extensible visitor. However, in this section, we will show how a reference-based visitor dispatcher can be built *on top of* the ownership-based version. Rather than requiring a separate mechanism, the reference-based version is actually a *specialization* of the existing system.
 
-In this section, we will walk through the detailed implementation of the reference-based visitor dispatcher. After this, you will hopefully appreciate how Rust's type system allows us to easily and safely extend the ownership-based visitor dispatcher to support references, all while making sure that there is no violation of lifetime constraints.
+We will now walk through how to implement the reference-based visitor dispatcher in detail. By the end, you will see how Rust’s type system enables us to safely and cleanly extend the original approach to support references, without compromising lifetime safety or clarity.
 
 ## Reference-Based Area Computation
 
-To demonstrate how reference-based visitor dispatching would work, let's define a new `HasAreaRef` trait that calculates the area with a `&self` reference:
+To demonstrate how reference-based visitor dispatch works, let’s define a new trait `HasAreaRef` that computes the area using a shared reference:
 
 ```rust
 pub trait HasAreaRef {
@@ -1015,9 +1015,9 @@ impl HasAreaRef for Rectangle { ... }
 impl HasAreaRef for Triangle { ... }
 ```
 
-In practice, there is probably no use to keep both `HasArea` and `HasAreaRef`. But to avoid confusion, we will name this version differently so that it is clear which version of area computation we are using.
+In practice, you probably wouldn’t need both `HasArea` and `HasAreaRef`, but for clarity we use a separate trait here to clearly distinguish between ownership-based and reference-based computations.
 
-Similarly, we use `#[cgp_computer]` to define a proxy `ComputeAreaRef` provider that implements `ComputerRef` using `HasAreaRef`:
+Next, we define a new provider `ComputeAreaRef` using `#[cgp_computer]`, which implements `ComputerRef` by calling `HasAreaRef`:
 
 ```rust
 #[cgp_computer]
@@ -1026,7 +1026,7 @@ fn compute_area_ref<T: HasAreaRef>(shape: &T) -> f64 {
 }
 ```
 
-With the reference-based area implementations in place, we can now implement `HasAreaRef` for `Shape` by simply using `MatchWithValueHandlersRef` instead of `MatchWithValueHandlers`:
+With this in place, we can now implement `HasAreaRef` for `Shape` by using `MatchWithValueHandlersRef`, the reference-based counterpart to `MatchWithValueHandlers`:
 
 ```rust
 impl HasAreaRef for Shape {
@@ -1036,7 +1036,7 @@ impl HasAreaRef for Shape {
 }
 ```
 
-Similarly, we can implement `HasAreaRef` for `ShapePlus` as easily with the same pattern:
+Likewise, the implementation for `ShapePlus` follows the same pattern:
 
 ```rust
 impl HasAreaRef for ShapePlus {
@@ -1046,26 +1046,28 @@ impl HasAreaRef for ShapePlus {
 }
 ```
 
-With the example, it may seem trivial to support reference-based visitor dispatch using `MatchWithValueHandlersRef`. In fact, as we will see next, this is indeed mostly true, aside from some additional complexities in handling generic lifetimes.
+At first glance, using `MatchWithValueHandlersRef` to enable reference-based dispatch may seem straightforward — and in many ways, it is. As we’ll see next, the core logic mirrors the ownership-based version closely, with only a few additional considerations around generic lifetimes.
 
-## PartialRef Variants
+## `PartialRef` Variants
 
-While most of the higher level implementations of reference-based extensible visitor is straightforward, we first need to also derive reference-based partial variants inside of `#[derive(ExtractField)]`. For instance, the following reference-based partial variants for `Shape` will be generated:
+Although most of the higher-level support for reference-based extensible visitors is relatively straightforward, we first need to generate reference-aware partial variants within `#[derive(ExtractField)]`. For example, for the `Shape` enum, the macro generates the following reference-based partial variants:
 
 ```rust
 pub enum PartialRefShape<'a, F0: MapType, F1: MapType> {
     Circle(F0::Map<&'a Circle>),
-    Rectangle(F0::Map<&'a Rectangle>),
+    Rectangle(F1::Map<&'a Rectangle>),
 }
 ```
 
-Compared to `PartialShape`, the definition of `PartialRefShape` contains an additional lifetime parameter `'a`, with each of its partial fields containing a `&'a` reference to the field value.
+Compared to the owned version `PartialShape`, the `PartialRefShape` definition introduces a lifetime parameter `'a`, and each of its fields now contains a reference with that lifetime. This allows us to safely operate on borrowed variants without taking ownership.
 
-The reason we need to define `PartialRefShape` in addition to `PartialShape` is because there is currently no easy way to express that a Rust value may "optionally" be a reference depending on the generic condition. As a result, it is more straightforward to have a separate enum that is parameterized by a lifetime. Furthermore, since the type is generated from macros and only used internally, this should not introduce too much overhead to the final implementation.
+We need a distinct `PartialRefShape` type rather than reusing `PartialShape` because Rust currently has no native mechanism to generically express a value that is either owned or borrowed based on some type-level condition. For instance, if Rust had a concept like a special `'owned` lifetime where `&'owned T` could be treated as just `T`, then it might be possible to unify the two representations. But such a feature does not exist, and arguably shouldn't.
+
+Given that limitation, the cleanest solution is to define a separate enum that introduces a lifetime parameter and holds references explicitly. Since these types are generated by macros and used internally within CGP's dispatching infrastructure, the added complexity is well-contained and does not burden the end user.
 
 ### `HasExtractorRef` Trait
 
-Aside from the partial-ref variants, we also need a new `HasExtractorRef` trait to extract from a reference to the full enum:
+In addition to the partial-ref variants, we need a new trait called `HasExtractorRef` that extracts data from a reference to the full enum:
 
 ```rust
 pub trait HasExtractorRef {
@@ -1077,9 +1079,9 @@ pub trait HasExtractorRef {
 }
 ```
 
-Compared to `HasExtractor`, `HasExtractorRef` has a generic associated type `ExtractorRef` that is parameterized by a lifetime `'a`, and with an additional constraint that `Self: 'a`. It also provides an `extractor_ref` method that takes a `&'a self` reference, and returns the partial-ref variants as `ExtractorRef<'a>`.
+Compared to the `HasExtractor` trait, `HasExtractorRef` introduces a generic associated type `ExtractorRef` that is parameterized by a lifetime `'a` and requires the constraint `Self: 'a`. It also defines an `extractor_ref` method that takes a `&'a self` reference and returns the corresponding partial-ref variants as `ExtractorRef<'a>`.
 
-Aside from the additional lifetime, the `HasExtractorRef` implementation for `Shape` is rather straightforwardly implemented as follows:
+Other than the addition of the lifetime parameter, implementing `HasExtractorRef` for `Shape` is straightforward, as shown below:
 
 ```rust
 impl HasExtractorRef for Shape {
@@ -1095,18 +1097,17 @@ impl HasExtractorRef for Shape {
 }
 ```
 
-Using `extractor_ref`, we can now extract from a borrowed `Shape`, without needing to perform cloning on each of its variant.
+With `extractor_ref`, it is now possible to extract data from a borrowed `Shape` without cloning each variant, enabling efficient reference-based dispatching.
 
 ### `ExtractField` Implementation
 
-Fortunately, aside from the partial-ref variants and the `HasExtractorRef` trait, we can mostly reuse all other traits and use them as if we are interacting with owned values. This is because `PartialRefShape` technically contains "owned" variant values that are in the form `&'a Circle`, `&'a Rectangle`, etc. For instance, we can implement `ExtractField` for `PartialRefShape` as follows:
+Fortunately, beyond the partial-ref variants and the `HasExtractorRef` trait, most other traits can be reused as if we were working with owned values. This works because `PartialRefShape` holds what are effectively "owned" variant values in the form of references like `&'a Circle` and `&'a Rectangle`. For example, we can implement `ExtractField` for `PartialRefShape` like this:
 
 ```rust
 impl<'a, F1: MapType> ExtractField<symbol!("Circle")> for PartialRefShape<'a, IsPresent, F1> {
     type Value = &'a Circle;
     type Remainder = PartialShape<'a, IsVoid, F1>;
 
-
     fn extract_field(
         self,
         _tag: PhantomData<symbol!("Circle")>,
@@ -1119,7 +1120,7 @@ impl<'a, F1: MapType> ExtractField<symbol!("Circle")> for PartialRefShape<'a, Is
 }
 ```
 
-The reason we can reuse traits like `ExtractField` is because the associated types like `Value` do not necessarily need to be the actual value from the original trait, and can also be a *reference* to the original value. So with this, we are pretending that the extensible variants contain reference for all its fields, and then manipulate them the same way as owned values.
+We can reuse traits like `ExtractField` because the associated types such as `Value` do not need to be the owned values themselves — they can be references to those values instead. This lets us treat extensible variants as if they contain references to their fields, allowing us to manipulate them just like owned values.
 
 ## `PromoteRef`
 

From c8122136df605c2079ea121c73d6e30e9ae76d70 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 08:47:38 +0000
Subject: [PATCH 10/21] Write on PromoteRef

---
 .../2025-07-20-extensible-datatypes-part-4.md | 80 +++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index c6e01a4..13c266a 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -1124,6 +1124,86 @@ We can reuse traits like `ExtractField` because the associated types such as `Va
 
 ## `PromoteRef`
 
+Just as we can make traits like `ExtractField` to work with borrowed fields, we can also make `Computer` works with borrowed inputs. In fact, the `#[cgp_computer]` macro expansion of the `compute_area_ref` function result in the following `Computer` implementation:
 
+```rust
+impl<Context, Code, T: HasAreaRef> Computer<Context, Code, &T> for ComputeAreaRef {
+    type Output = f64;
+
+    fn compute(_context: &Context, _code: PhantomData<Code>, shape: &T) -> f64 {
+        compute_area_ref(shape)
+    }
+}
+```
+
+As we can see, in the `Computer` implementation for `ComputeAreaRef`, our implementation works with any reference `&T` instead of owned `T` as the input type, given that `T` implements `HasAreaRef`.
+
+This shows that technically, the `Computer` trait alone is sufficient to support all kinds of inputs, including borrowed inputs. However, to improve developer ergonomics, CGP also provides the `ComputerRef` trait so that the user don't need to worry about specifying `&T` in the input type parameter. Furthermore, `ComputerRef` can save us from using higher-ranked trait bounds inside where clauses and input-based delegation. As a result, it is much better suited when we want to work with borrowed inputs.
+
+CGP also provides a `PromoteRef` adapter, to convert a `Computer` provider to/from a `ComputerRef` provider. For example, the `ComputerRef` implementation for `ComputeAreaRef` is simply defined as:
+
+```rust
+delegate_components! {
+    ComputeAreaRef {
+        ComputerRefComponent: PromoteRef<Self>,
+    }
+}
+```
+
+What this means is that `ComputeAreaRef` implements `ComputerRef` through `PromoteRef<ComputeAreaRef>`. Behind the scene, `PromoteRef` is implemented as follows:
+
+```rust
+#[cgp_provider]
+impl<Context, Code, Input, Provider, Output> ComputerRef<Context, Code, Input>
+    for PromoteRef<Provider>
+where
+    Provider: for<'a> Computer<Context, Code, &'a Input, Output = Output>,
+{
+    type Output = Output;
+
+    fn compute_ref(context: &Context, tag: PhantomData<Code>, input: &Input) -> Self::Output {
+        Provider::compute(context, tag, input)
+    }
+}
+```
+
+In the implementation above, we can see that `PromoteRef` implements `ComputerRef` for a `Provider`, if the provider has a higher-ranked trait bound `for<'a>` that implements `Computer` for all `&'a Input` reference. Through `PromoteRef`, this higher-ranked trait bound is hidden away so that the end users don't need to be aware of their existence.
+
+`PromoteRef` also implements `Computer` by wrapping a `Provider` that implements `ComputerRef`, shown as follows:
+
+```rust
+#[cgp_provider]
+impl<Context, Code, Input, Provider> Computer<Context, Code, &Input> for PromoteRef<Provider>
+where
+    Provider: ComputerRef<Context, Code, Input>,
+{
+    type Output = Provider::Output;
+
+    fn compute(context: &Context, tag: PhantomData<Code>, input: &Input) -> Self::Output {
+        Provider::compute_ref(context, tag, input)
+    }
+}
+```
+
+Here, `PromoteRef` implements `Computer` that works with any `&Input` reference, provided that the inner `Provider` implements `ComputerRef` for `Input`.
+
+With both implementations in place, `PromoteRef` offers bidirectional transformation between `Computer` and `ComputerRef`. This allows us to easily convert the computer providers, depending on which form of interface we want to use.
+
+## `MatchWithValueHandlersRef`
+
+To support reference-based dispatching, aside from the few reference-specific constructs that we have introduced earlier, the remaining implementation of `MatchWithValueHandlersRef` is remarkably almost as simple as the original implementation of `MatchWithValueHandlers`:
+
+```rust
+pub type MatchWithValueHandlersRef<Provider> =
+    UseInputDelegate<MatchWithFieldHandlersInputsRef<HandleFieldValue<PromoteRef<Provider>>>>;
+
+delegate_components! {
+    <Input: HasFieldHandlers<Provider>, Provider>
+    new MatchWithFieldHandlersInputsRef<Provider> {
+        Input:
+            PromoteRef<MatchWithHandlersRef<Input::Handlers>>
+    }
+}
+```
 
 # Conclusion

From 877d9546c6bbc3abe1790ccd487c54977213a576 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 13:12:33 +0000
Subject: [PATCH 11/21] Write MatchWithHandlersRef section

---
 .../2025-07-20-extensible-datatypes-part-4.md | 100 +++++++++++++++---
 1 file changed, 85 insertions(+), 15 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 13c266a..d0453bb 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -19,6 +19,8 @@ This duality is not just theoretical — it has practical implications for softw
 
 With this in mind, we’ll now explore the CGP constructs that support extensible variants. As you go through the examples and implementation details, we encourage you to look for the parallels and contrasts with extensible records.
 
+---
+
 # Base Implementation
 
 ## `FromVariant` Trait
@@ -287,6 +289,58 @@ Here, we use an empty `match` expression on `self`, which works because the comp
 
 By leveraging the `Void` type in this way, CGP allows us to exhaustively extract every variant from a partial enum and confidently conclude that no cases remain. This eliminates the need for runtime assertions, unreachable branches, or panics. Instead, the type system itself guarantees that all variants have been handled, enabling a clean and fully type-safe approach to enum decomposition.
 
+### `FinalizeExtractResult` Trait
+
+When we have a result type in the form `Result<Output, Remainder>`, with the `Remainder` being inhabitable, it would be convenient if we have a helper method for us to directly take the `Output` out from the `Result`. To do this, we define the `FinalizeExtractResult` helper trait, which is defined with a blanket implementation as follows:
+
+```rust
+pub trait FinalizeExtractResult {
+    type Output;
+
+    fn finalize_extract_result(self) -> Self::Output;
+}
+
+impl<T, E> FinalizeExtractResult for Result<T, E>
+where
+    E: FinalizeExtract,
+{
+    type Output = T;
+
+    fn finalize_extract_result(self) -> T {
+        match self {
+            Ok(value) => value,
+            Err(remainder) => remainder.finalize_extract(),
+        }
+    }
+}
+```
+
+With `FinalizeExtractResult`, we can call `result.finalize_extract_result()` to get the output, if the remainder implements `FinalizeExtract`. With this, we can for example simplify the implementation of the `compute_area` function earlier to:
+
+```rust
+pub fn compute_area(shape: Shape) -> f64 {
+    match shape
+        .to_extractor()
+        .extract_field(PhantomData::<symbol!("Circle")>)
+    {
+        Ok(circle) => PI * circle.radius * circle.radius,
+        Err(remainder) => {
+            let rectangle = remainder
+                .extract_field(PhantomData::<symbol!("Rectangle")>)
+                .finalize_extract_result();
+
+            rectangle.width * rectangle.height
+        }
+    }
+}
+```
+
+When handling the remainder after the `Circle` variant was extracted, we use `finalize_extract_result` after calling `extract_field` to get the `Rectangle` variant.
+
+This small ergonomic improvement can be useful when we perform generic finalization later on, so that we don't need to manually match and call `finalize_result` on a generic remainder.
+
+---
+
 # Implementation of Casts
 
 With the foundational traits for extensible variants in place, we can now explore how to implement the `CanUpcast` and `CanDowncast` traits. These traits enable safe and generic upcasting and downcasting between enums that share compatible variants.
@@ -355,17 +409,14 @@ where
     Remainder: FinalizeExtract,
 {
     fn upcast(self, _tag: PhantomData<Target>) -> Target {
-        match Context::Fields::extract_from(self.to_extractor()) {
-            Ok(target) => target,
-            Err(remainder) => remainder.finalize_extract(),
-        }
+        Context::Fields::extract_from(self.to_extractor()).finalize_extract_result()
     }
 }
 ```
 
 Here’s how it works. First, the `Context` type (the source enum) must implement both `HasFields` and `HasExtractor`. The `HasFields` trait provides a type-level sum of variants, and `HasExtractor` converts the enum into its corresponding partial variants. Next, the associated `Fields` type must implement the helper trait `FieldsExtractor`, which handles the actual extraction of variants into the target type. The `Remainder` returned by this operation must then implement `FinalizeExtract`, which guarantees that all source variants have been accounted for.
 
-In the method body, we begin by calling `self.to_extractor()` to convert the source enum into a value with partial variants. We then use `Fields::extract_from` to extract the relevant variants into the target enum. Finally, we call `finalize_extract()` on the remainder.
+In the method body, we begin by calling `self.to_extractor()` to convert the source enum into a value with partial variants. We then use `Fields::extract_from` to extract the relevant variants into the target enum. Finally, we call `finalize_extract_result()` to discharge the remainder in `Err`, and return the `Target` result in `Ok`.
 
 ## `FieldsExtractor` Trait
 
@@ -563,22 +614,15 @@ where
 {
     type Output = Output;
 
-    fn compute(_context: &Context, code: PhantomData<Code>, input: Input) -> Output {
-        let res = DispatchMatchers::compute(_context, code, input.to_extractor());
-
-        match res {
-            Ok(output) => output,
-            Err(remainder) => remainder.finalize_extract(),
-        }
+    fn compute(context: &Context, code: PhantomData<Code>, input: Input) -> Output {
+        DispatchMatchers::compute(context, code, input.to_extractor()).finalize_extract_result()
     }
 }
 ```
 
 The `MatchWithHandlers` provider is parameterized by a `Handlers` type, which represents a type-level list of visitor handlers responsible for processing the variants of a generic `Input` enum. The implementation requires `Input` to implement the `HasExtractor` trait, which provides access to its partial variants.
 
-Within the `compute` method, we first convert the input into its extractor form using `input.to_extractor()`. This partial variant is then passed to the lower-level dispatcher `DispatchMatchers<Handlers>`, which attempts to match and handle each variant. It returns a `Result<Output, Remainder>`, where a successful match produces an `Output`, and an unmatched remainder is returned otherwise.
-
-If the result is `Ok`, the output is returned directly. If the result is `Err`, we call `finalize_extract()` on the remainder to assert that it is uninhabited, ensuring that all variants have been exhaustively handled. This makes the case both safe and unreachable, as guaranteed by the type system.
+Within the `compute` method, we first convert the input into its extractor form using `input.to_extractor()`. This partial variant is then passed to the lower-level dispatcher `DispatchMatchers<Handlers>`, which attempts to match and handle each variant. It returns a `Result<Output, Remainder>`, where a successful match produces an `Output`, and an unmatched remainder is returned otherwise. But since `Remainder` is expected to implement `FinalizeExtract`, we can call `finalize_extract_result()` to return the `Output` directly.
 
 ### `DispatchMatchers`
 
@@ -1122,6 +1166,32 @@ impl<'a, F1: MapType> ExtractField<symbol!("Circle")> for PartialRefShape<'a, Is
 
 We can reuse traits like `ExtractField` because the associated types such as `Value` do not need to be the owned values themselves — they can be references to those values instead. This lets us treat extensible variants as if they contain references to their fields, allowing us to manipulate them just like owned values.
 
+## `MatchWithHandlersRef`
+
+Since reference-based dispatching requires the use of `HasExtractorRef`, we also need to implement variations of downstream handlers like `MatchWithHandlers` to use `HasExtractorRef` instead of `HasExtractor`. This is implemented as `MatchWithHandlersRef` as follows:
+
+```rust
+#[cgp_provider]
+impl<'a, Context, Code, Input, Output, Remainder, Handlers> Computer<Context, Code, &'a Input>
+    for MatchWithHandlersRef<Handlers>
+where
+    Input: HasExtractorRef,
+    DispatchMatchers<Handlers>:
+        Computer<Context, Code, Input::ExtractorRef<'a>, Output = Result<Output, Remainder>>,
+    Remainder: FinalizeExtract,
+{
+    type Output = Output;
+
+    fn compute(context: &Context, code: PhantomData<Code>, input: &'a Input) -> Output {
+        DispatchMatchers::compute(context, code, input.extractor_ref()).finalize_extract_result()
+    }
+}
+```
+
+`MatchWithHandlersRef` implements `Computer` with a reference `&'a Input`. To perform the extraction, it requires `Input` to implement `HasExtractorRef`. It then pass `Input::ExtractorRef<'a>` as the input to `DispatchMatchers`, which performs the same monadic pipeline computation as before. Finally, we expect the `Remainder` returned to implement `FinalizeExtract`, and use `finalize_extract_result` to extract the `Output`.
+
+It is worth noticing that despite working with references, `MatchWithHandlersRef` implements `Computer` and not `ComputerRef`. Similarly, when `DispatchMatchers` is used, the monadic pipeline also expects each handler to implement `Computer` and not `ComputerRef`. This means that in order to work with reference-based visitor dispatching, we need to first "convert" all providers that implement `ComputerRef`, turn them into providers that implement `Computer`, and finally "convert" `MatchWithHandlersRef` back to implementing `ComputerRef`.
+
 ## `PromoteRef`
 
 Just as we can make traits like `ExtractField` to work with borrowed fields, we can also make `Computer` works with borrowed inputs. In fact, the `#[cgp_computer]` macro expansion of the `compute_area_ref` function result in the following `Computer` implementation:

From 5bf906872872d1e001859c592af5d83766f845cb Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 13:23:16 +0000
Subject: [PATCH 12/21] Update PromoteRef section

---
 .../blog/2025-07-20-extensible-datatypes-part-4.md | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index d0453bb..6b82fd3 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -1220,7 +1220,11 @@ delegate_components! {
 }
 ```
 
-What this means is that `ComputeAreaRef` implements `ComputerRef` through `PromoteRef<ComputeAreaRef>`. Behind the scene, `PromoteRef` is implemented as follows:
+What this means is that `ComputeAreaRef` implements `ComputerRef` through `PromoteRef<ComputeAreaRef>`.
+
+### Promotion from `ComputerRef` to `Computer`
+
+Behind the scene, `PromoteRef` is implemented as follows:
 
 ```rust
 #[cgp_provider]
@@ -1239,6 +1243,12 @@ where
 
 In the implementation above, we can see that `PromoteRef` implements `ComputerRef` for a `Provider`, if the provider has a higher-ranked trait bound `for<'a>` that implements `Computer` for all `&'a Input` reference. Through `PromoteRef`, this higher-ranked trait bound is hidden away so that the end users don't need to be aware of their existence.
 
+It is worth noticing that `PromoteRef` also requires the inner `Computer` provider to always have the same `Output` type, regardless of which lifetime `'a` is used for the input. This has an important implication that the `ComputerRef` promotion would *not* work if the `Output` type *borrows* some value within `&'a Input`.
+
+This restriction follows from the design of `ComputerRef`, which has a singular `Output` type that is independent of the lifetime of the input reference in the `compute_ref` method. In some sense, this means that `ComputerRef` is more restrictive and cannot represent all computations that could be implemented through `Computer`. Nevertheless, in case if the user really needs to return `Output` that depends on the input lifetime, they can always revert to using `Computer` directly for such use cases.
+
+### Promotion from `Computer` to `ComputerRef`
+
 `PromoteRef` also implements `Computer` by wrapping a `Provider` that implements `ComputerRef`, shown as follows:
 
 ```rust
@@ -1261,7 +1271,7 @@ With both implementations in place, `PromoteRef` offers bidirectional transforma
 
 ## `MatchWithValueHandlersRef`
 
-To support reference-based dispatching, aside from the few reference-specific constructs that we have introduced earlier, the remaining implementation of `MatchWithValueHandlersRef` is remarkably almost as simple as the original implementation of `MatchWithValueHandlers`:
+To support reference-based dispatching, aside from the few reference-specific constructs that we have introduced earlier, the remaining implementation of `MatchWithValueHandlersRef` is almost as simple as the original implementation of `MatchWithValueHandlers`:
 
 ```rust
 pub type MatchWithValueHandlersRef<Provider> =

From 8ed7b89fa5352b4d582b10a96d0c698cedb7dbae Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 14:03:26 +0000
Subject: [PATCH 13/21] Write MatchWithValueHandlersRef section

---
 .../2025-07-20-extensible-datatypes-part-4.md | 50 ++++++++++++++++++-
 1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 6b82fd3..9bac79b 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -1222,7 +1222,7 @@ delegate_components! {
 
 What this means is that `ComputeAreaRef` implements `ComputerRef` through `PromoteRef<ComputeAreaRef>`.
 
-### Promotion from `ComputerRef` to `Computer`
+### Promotion from `Computer` to `ComputerRef`
 
 Behind the scene, `PromoteRef` is implemented as follows:
 
@@ -1247,7 +1247,7 @@ It is worth noticing that `PromoteRef` also requires the inner `Computer` provid
 
 This restriction follows from the design of `ComputerRef`, which has a singular `Output` type that is independent of the lifetime of the input reference in the `compute_ref` method. In some sense, this means that `ComputerRef` is more restrictive and cannot represent all computations that could be implemented through `Computer`. Nevertheless, in case if the user really needs to return `Output` that depends on the input lifetime, they can always revert to using `Computer` directly for such use cases.
 
-### Promotion from `Computer` to `ComputerRef`
+### Promotion from `ComputerRef` to `Computer`
 
 `PromoteRef` also implements `Computer` by wrapping a `Provider` that implements `ComputerRef`, shown as follows:
 
@@ -1286,4 +1286,50 @@ delegate_components! {
 }
 ```
 
+If we compare the definition with `MatchWithValueHandlers`, we could notice that the main difference is that `MatchWithFieldHandlersInputsRef` is used, in addition to some wrapping and unwrapping using `PromoteRef`.
+
+### Example use of `MatchWithValueHandlersRef`
+
+To understand what is really happening, let's try to trace the computation flow of what happens when we call `MatchWithValueHandlersRef<ComputeAreaRef>` on `Shape`:
+
+```rust
+impl HasAreaRef for Shape {
+    fn area(&self) -> f64 {
+        MatchWithValueHandlersRef::<ComputeAreaRef>::compute_ref(&(), PhantomData::<()>, self)
+    }
+}
+```
+
+- The `Provider` argument to `MatchWithHandlers` is `ComputeAreaRef`. This is expanded into `HandleFieldValue<PromoteRef<ComputeAreaRef>>` when passed to `MatchWithFieldHandlersInputsRef`.
+- `UseInputDelegate` is expected to implement `ComputerRef` with `Input` being `Shape`, which is dispatched to `MatchWithFieldHandlersInputsRef`.
+- `MatchWithFieldHandlersInputsRef` is called with `Input` being `Shape`, and `Provider` being `HandleFieldValue<PromoteRef<ComputeAreaRef>>`.
+- `Shape` is expected to implement `HasFieldHandlers<HandleFieldValue<PromoteRef<ComputeAreaRef>>>`, with `Shape::Handlers` expanding into:
+    ```rust
+    Product![
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+    ]
+    ```
+- The delegate entry maps to `PromoteRef<MatchWithHandlersRef<Input::Handlers>>`, which expands into:
+    ```rust
+    PromoteRef<MatchWithHandlersRef<Product![
+        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+    ]>
+    ```
+- To implement `ComputerRef`, `PromoteRef<MatchWithHandlersRef<Input::Handlers>>` requires `MatchWithHandlersRef<Input::Handlers>` to implement `Computer`.
+- To implement `Computer`, `MatchWithHandlersRef<Input::Handlers>` requires `HandleFieldValue<PromoteRef<ComputeAreaRef>>` to implement the following constraints:
+    - `Computer<(), (), Field<symbol!("Circle"), &Circle>>`
+    - `Computer<(), (), Field<symbol!("Rectangle"), &Rectangle>>`
+- After unwrapping the field value by `HandleFieldValue`, the inner provider `PromoteRef<ComputeAreaRef>` needs to implement the following constraints:
+    - `Computer<(), (), &Circle>`
+    - `Computer<(), (), &Rectangle>`
+- To implement `Computer`, `PromoteRef` requires `ComputeAreaRef` to implement the following constraints:
+    - `ComputerRef<(), (), Circle>`
+    - `ComputerRef<(), (), Rectangle>`
+
+As we can see, the crucial difference in the implementation of `MatchWithValueHandlersRef` is that we need to know when is the right time to use `Computer` vs `ComputerRef`, and how to use `PromoteRef` at the right place to facilitate the conversion between `Computer` and `ComputerRef`.
+
+If this is still confusing to you, don't worry too much, as the implementation details are mostly shielded away from how it is used. The main takeaway from this exercise that aside from a few reference-specific definitions, most of the work of reference-based extensible visitor shares the same underlying implementation as the ownership-based implementation.
+
 # Conclusion

From 54279e28bf422a57a9445348e1493b49b80debf3 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 15:59:32 +0000
Subject: [PATCH 14/21] Add section on UseContext

---
 .../2025-07-20-extensible-datatypes-part-4.md | 70 +++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-20-extensible-datatypes-part-4.md
index 9bac79b..9388b54 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-20-extensible-datatypes-part-4.md
@@ -1030,6 +1030,76 @@ impl HasArea for ShapePlus {
 
 Although some boilerplate still remains, this approach is significantly simpler than manually matching each variant or relying on procedural macros. It also brings more flexibility and type safety. In the future, CGP may provide more ergonomic layers on top of this pattern, making common use cases like `HasArea` even easier to express.
 
+## Dispatch to Context
+
+In the earlier definition of `MatchWithValueHandlers`, we omitted a minor detail which is that it has a *default parameter* for `Provider`:
+
+```rust
+pub type MatchWithValueHandlers<Provider = UseContext> =
+    UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<Provider>>>;
+```
+
+When no generic parameter is specified, `MatchWithValueHandlers` will by default use `UseContext` as the provider for `Computer`. If you have read earlier blog posts, you might recall that `UseContext` is a generic provider that implements the provider trait by using the consumer trait implementation from the context. For instance, the `UseContext` implementation for `Computer` is as follows:
+
+```rust
+#[cgp_provider]
+impl<Context, Code, Input> Computer<Context, Code, Input> for UseContext
+where
+    Context: CanCompute<Code, Input>,
+{
+    type Output = Context::Output;
+
+    fn compute(context: &Context, code: PhantomData<Code>, input: Input) -> Self::Output {
+        context.compute(code, input)
+    }
+}
+```
+
+It is useful to use `UseContext` as the provider for visitor dispatchers like `MatchWithValueHandlers`, as we can let the concrete context decide on how to handle each variant differently. With it, we can for example implement an `App` context that implements `Computer` for `Shape` and `ShapePlus` as follows:
+
+```rust
+#[cgp_context]
+pub struct App;
+
+delegate_components! {
+    AppComponents {
+        ComputerComponent: UseInputDelegate<new AreaComputers {
+            [
+                Circle,
+                Rectangle,
+                Triangle,
+            ]:
+                ComputeArea,
+            [
+                Shape,
+                ShapePlus,
+            ]: MatchWithValueHandlers,
+        }>
+    }
+}
+```
+
+In our example `App` implementation, we implement `Computer` through `UseInputDelegate`, with the variants `Circle`, `Rectangle`, and `Triangle` being handled by `ComputeArea`. As for `Shape` and `ShapePlus`, we use `MatchWithValueHandlers` without specifying any `Provider` parameter, and thus defaulting it to `UseContext`.
+
+With this configuration, when `MatchWithValueHandlers` attempts to dispatch the handling of each variant, it will try to do that via `App` instead of directly calling `ComputeArea`. This indirection allows us to customize the handling of each variant much more easily. For example, suppose that we have an optimized way of computing the area of `Circle` with less multiplication, we can easily switch to that implementation by updating the wiring in `AppComponents`.
+
+```rust
+delegate_components! {
+    AppComponents {
+        ComputerComponent: UseInputDelegate<new AreaComputers {
+            Circle: OptimizedCircleArea,
+            ...
+        }>
+    }
+}
+```
+
+This kind of customization is much harder to achieve, if we tightly couple the dispatcher with non-CGP traits such as `HasArea`, such as in `MatchWithValueHandlers<ComputeArea>`. In this case, the only way to customize the implementation is to directly modify the `HasArea` implementation of `Circle`, which would require ownership to either the `Circle` type or the `HasArea` trait.
+
+Granted, this level of modularity is probably not needed for this simple example of calculating the area of a shape. But as we have seen in the examples in [part 2](/blog/extensible-datatypes-part-2), this is essential for more complex use cases such as building modular interpreters.
+
+On the other hand, by proxying the handling of variant through `UseContext`, we are able to override the parameter to `MatchWithValueHandlers`, so that we can use a custom provider with it, such as with `ComputeArea`. In other words, this optional parameter allows us to *both* delegate the variant handling to the context, and also to custom providers when we don't want to go through the context. This flexibility allows us to also use `MatchWithValueHandlers` without any additional wiring to the context, such as when we use `()` as the context with `MatchWithValueHandlers<ComputeArea>`.
+
 ---
 
 # Visitor Dispatcher by Reference

From e2da4852943563ec63595eaa8f75a835decc0e52 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 19:43:33 +0000
Subject: [PATCH 15/21] AI-revise sections

---
 ...2025-07-30-extensible-datatypes-part-4.md} | 138 ++++++++++--------
 1 file changed, 74 insertions(+), 64 deletions(-)
 rename content/blog/{2025-07-20-extensible-datatypes-part-4.md => 2025-07-30-extensible-datatypes-part-4.md} (85%)

diff --git a/content/blog/2025-07-20-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
similarity index 85%
rename from content/blog/2025-07-20-extensible-datatypes-part-4.md
rename to content/blog/2025-07-30-extensible-datatypes-part-4.md
index 9388b54..ef96e88 100644
--- a/content/blog/2025-07-20-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -288,10 +288,9 @@ impl FinalizeExtract for PartialShape<IsVoid, IsVoid> {
 Here, we use an empty `match` expression on `self`, which works because the compiler knows that `PartialShape<IsVoid, IsVoid>` has no possible value. Since it is impossible to construct such a value, the match is guaranteed to be unreachable. Rust verifies this at compile time, ensuring both safety and correctness.
 
 By leveraging the `Void` type in this way, CGP allows us to exhaustively extract every variant from a partial enum and confidently conclude that no cases remain. This eliminates the need for runtime assertions, unreachable branches, or panics. Instead, the type system itself guarantees that all variants have been handled, enabling a clean and fully type-safe approach to enum decomposition.
-
 ### `FinalizeExtractResult` Trait
 
-When we have a result type in the form `Result<Output, Remainder>`, with the `Remainder` being inhabitable, it would be convenient if we have a helper method for us to directly take the `Output` out from the `Result`. To do this, we define the `FinalizeExtractResult` helper trait, which is defined with a blanket implementation as follows:
+When working with results of type `Result<Output, Remainder>`, where the `Remainder` type is guaranteed to be inhabitable, it is often useful to have a convenient way to directly extract the `Output` value. To achieve this, CGP defines the `FinalizeExtractResult` trait, which provides a helper method to finalize and unwrap such results. Its definition includes a blanket implementation for all `Result` types where the error type implements `FinalizeExtract`:
 
 ```rust
 pub trait FinalizeExtractResult {
@@ -315,7 +314,7 @@ where
 }
 ```
 
-With `FinalizeExtractResult`, we can call `result.finalize_extract_result()` to get the output, if the remainder implements `FinalizeExtract`. With this, we can for example simplify the implementation of the `compute_area` function earlier to:
+With `FinalizeExtractResult`, any result value can call `finalize_extract_result()` to obtain the `Output` directly, as long as the remainder type implements `FinalizeExtract`. This allows functions that work with extractable variants to become simpler and more readable. For example, the implementation of `compute_area` can be written as:
 
 ```rust
 pub fn compute_area(shape: Shape) -> f64 {
@@ -335,9 +334,9 @@ pub fn compute_area(shape: Shape) -> f64 {
 }
 ```
 
-When handling the remainder after the `Circle` variant was extracted, we use `finalize_extract_result` after calling `extract_field` to get the `Rectangle` variant.
+When handling the remainder after the `Circle` variant was extracted, we use `finalize_extract_result` after calling `remainder.extract_field()` to get the `Rectangle` variant.
 
-This small ergonomic improvement can be useful when we perform generic finalization later on, so that we don't need to manually match and call `finalize_result` on a generic remainder.
+This trait provides a small but valuable ergonomic improvement, especially when performing generic extractions and finalizations. It allows developers to avoid repetitive pattern matching and ensures that the final output can be obtained with a single, clear method call.
 
 ---
 
@@ -1030,16 +1029,16 @@ impl HasArea for ShapePlus {
 
 Although some boilerplate still remains, this approach is significantly simpler than manually matching each variant or relying on procedural macros. It also brings more flexibility and type safety. In the future, CGP may provide more ergonomic layers on top of this pattern, making common use cases like `HasArea` even easier to express.
 
-## Dispatch to Context
+## Dispatching to Context
 
-In the earlier definition of `MatchWithValueHandlers`, we omitted a minor detail which is that it has a *default parameter* for `Provider`:
+In the earlier definition of `MatchWithValueHandlers`, we omitted one detail that it has a default type parameter for `Provider`:
 
 ```rust
 pub type MatchWithValueHandlers<Provider = UseContext> =
     UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<Provider>>>;
 ```
 
-When no generic parameter is specified, `MatchWithValueHandlers` will by default use `UseContext` as the provider for `Computer`. If you have read earlier blog posts, you might recall that `UseContext` is a generic provider that implements the provider trait by using the consumer trait implementation from the context. For instance, the `UseContext` implementation for `Computer` is as follows:
+This means that if no generic parameter is specified, `MatchWithValueHandlers` will default to using `UseContext` as the provider for `Computer`. If you’ve read the earlier blog posts, you may recall that `UseContext` is a generic provider that delegates its behavior to the consumer trait implementation from the given context. For example, the implementation of `Computer` for `UseContext` looks like this:
 
 ```rust
 #[cgp_provider]
@@ -1055,7 +1054,7 @@ where
 }
 ```
 
-It is useful to use `UseContext` as the provider for visitor dispatchers like `MatchWithValueHandlers`, as we can let the concrete context decide on how to handle each variant differently. With it, we can for example implement an `App` context that implements `Computer` for `Shape` and `ShapePlus` as follows:
+Using `UseContext` as the provider for dispatchers like `MatchWithValueHandlers` is especially useful because it enables the concrete context to define how each variant should be handled. For example, we can implement an `App` context that provides `Computer` implementations for various shapes like `Shape` and `ShapePlus`:
 
 ```rust
 #[cgp_context]
@@ -1079,9 +1078,9 @@ delegate_components! {
 }
 ```
 
-In our example `App` implementation, we implement `Computer` through `UseInputDelegate`, with the variants `Circle`, `Rectangle`, and `Triangle` being handled by `ComputeArea`. As for `Shape` and `ShapePlus`, we use `MatchWithValueHandlers` without specifying any `Provider` parameter, and thus defaulting it to `UseContext`.
+In this example, the `App` context uses `UseInputDelegate` to define how to compute areas. Variants like `Circle`, `Rectangle`, and `Triangle` are directly handled by `ComputeArea`. For `Shape` and `ShapePlus`, we use `MatchWithValueHandlers` without specifying a `Provider`, which means it defaults to `UseContext`.
 
-With this configuration, when `MatchWithValueHandlers` attempts to dispatch the handling of each variant, it will try to do that via `App` instead of directly calling `ComputeArea`. This indirection allows us to customize the handling of each variant much more easily. For example, suppose that we have an optimized way of computing the area of `Circle` with less multiplication, we can easily switch to that implementation by updating the wiring in `AppComponents`.
+With this setup, when `MatchWithValueHandlers` dispatches to the individual variants, it delegates to `App` itself instead of calling `ComputeArea` directly. This allows us to override the behavior of individual variants easily. For instance, if we want to replace the implementation for `Circle` with an optimized version, we can simply change the wiring in `AppComponents`:
 
 ```rust
 delegate_components! {
@@ -1094,11 +1093,15 @@ delegate_components! {
 }
 ```
 
-This kind of customization is much harder to achieve, if we tightly couple the dispatcher with non-CGP traits such as `HasArea`, such as in `MatchWithValueHandlers<ComputeArea>`. In this case, the only way to customize the implementation is to directly modify the `HasArea` implementation of `Circle`, which would require ownership to either the `Circle` type or the `HasArea` trait.
+### The Flexibility of `UseContext`
+
+This kind of customization would be much harder to achieve if the dispatcher were tightly coupled to a concrete trait implementation, such as using `MatchWithValueHandlers<ComputeArea>` directly. In that case, the only way to change the behavior would be to modify the `HasArea` implementation for `Circle`, which would require ownership of either the `Circle` type or the `HasArea` trait.
+
+While this level of indirection may seem unnecessary for a simple example like computing the area of a shape, it becomes crucial in more complex scenarios, such as the modular interpreter design discussed in [part 2](/blog/extensible-datatypes-part-2).
 
-Granted, this level of modularity is probably not needed for this simple example of calculating the area of a shape. But as we have seen in the examples in [part 2](/blog/extensible-datatypes-part-2), this is essential for more complex use cases such as building modular interpreters.
+By routing the variant handling through `UseContext`, we also retain the flexibility to override the provider entirely. That means we can use `MatchWithValueHandlers<ComputeArea>` in cases where we don’t want to go through a `Context` at all, such as using `()` as the context. This optional `Provider` parameter gives us the best of both worlds: we can let the context provide the necessary wiring when needed, or directly specify a concrete provider when that makes more sense.
 
-On the other hand, by proxying the handling of variant through `UseContext`, we are able to override the parameter to `MatchWithValueHandlers`, so that we can use a custom provider with it, such as with `ComputeArea`. In other words, this optional parameter allows us to *both* delegate the variant handling to the context, and also to custom providers when we don't want to go through the context. This flexibility allows us to also use `MatchWithValueHandlers` without any additional wiring to the context, such as when we use `()` as the context with `MatchWithValueHandlers<ComputeArea>`.
+This pattern of using a provider parameter that defaults to `UseContext` is a recurring design strategy in CGP. It offers a powerful level of control, letting developers customize behavior in a modular and extensible way depending on their use case.
 
 ---
 
@@ -1238,7 +1241,7 @@ We can reuse traits like `ExtractField` because the associated types such as `Va
 
 ## `MatchWithHandlersRef`
 
-Since reference-based dispatching requires the use of `HasExtractorRef`, we also need to implement variations of downstream handlers like `MatchWithHandlers` to use `HasExtractorRef` instead of `HasExtractor`. This is implemented as `MatchWithHandlersRef` as follows:
+Because reference-based dispatching relies on `HasExtractorRef`, we also need to adapt downstream constructs like `MatchWithHandlers` to work with references instead of owned values. This adaptation is provided by `MatchWithHandlersRef`, which uses `HasExtractorRef` in place of `HasExtractor`:
 
 ```rust
 #[cgp_provider]
@@ -1258,13 +1261,15 @@ where
 }
 ```
 
-`MatchWithHandlersRef` implements `Computer` with a reference `&'a Input`. To perform the extraction, it requires `Input` to implement `HasExtractorRef`. It then pass `Input::ExtractorRef<'a>` as the input to `DispatchMatchers`, which performs the same monadic pipeline computation as before. Finally, we expect the `Remainder` returned to implement `FinalizeExtract`, and use `finalize_extract_result` to extract the `Output`.
+In this implementation, `MatchWithHandlersRef` handles `Computer` over a borrowed input `&'a Input`. It requires the input type to implement `HasExtractorRef` so it can extract the appropriate partial-ref variant. The extracted value, which is of type `Input::ExtractorRef<'a>`, is then passed to `DispatchMatchers`, which processes it using the same monadic pipeline as in the owned-value case. After dispatching to the handlers, the `Output` from `Result<Output, Remainder>` is extracted by calling `finalize_extract_result`, which relies on the `Remainder` type to implement `FinalizeExtract`.
 
-It is worth noticing that despite working with references, `MatchWithHandlersRef` implements `Computer` and not `ComputerRef`. Similarly, when `DispatchMatchers` is used, the monadic pipeline also expects each handler to implement `Computer` and not `ComputerRef`. This means that in order to work with reference-based visitor dispatching, we need to first "convert" all providers that implement `ComputerRef`, turn them into providers that implement `Computer`, and finally "convert" `MatchWithHandlersRef` back to implementing `ComputerRef`.
+One subtle but important point is that `MatchWithHandlersRef` still implements `Computer` rather than `ComputerRef`. The same is true for the handlers invoked through `DispatchMatchers`, which also expect `Computer` implementations. As a result, reference-based visitor dispatching requires an intermediate conversion step. Variant handlers that originally implement `ComputerRef` must first be "lifted" into providers implementing `Computer`.
+
+After constructing the reference-based pipeline, `MatchWithHandlersRef` can then unlift the entire pipeline to implement `ComputerRef`. This layered approach ensures that reference-based dispatching reuses the same infrastructure as the ownership-based version, while preserving type safety and proper lifetime handling.
 
 ## `PromoteRef`
 
-Just as we can make traits like `ExtractField` to work with borrowed fields, we can also make `Computer` works with borrowed inputs. In fact, the `#[cgp_computer]` macro expansion of the `compute_area_ref` function result in the following `Computer` implementation:
+In the same way that traits like `ExtractField` can operate on borrowed fields, the `Computer` trait can also work with borrowed inputs. In fact, the `#[cgp_computer]` macro expansion for the `compute_area_ref` function produces the following `Computer` implementation:
 
 ```rust
 impl<Context, Code, T: HasAreaRef> Computer<Context, Code, &T> for ComputeAreaRef {
@@ -1276,11 +1281,11 @@ impl<Context, Code, T: HasAreaRef> Computer<Context, Code, &T> for ComputeAreaRe
 }
 ```
 
-As we can see, in the `Computer` implementation for `ComputeAreaRef`, our implementation works with any reference `&T` instead of owned `T` as the input type, given that `T` implements `HasAreaRef`.
+Here, the `Computer` implementation for `ComputeAreaRef` accepts any reference `&T` as the input type, as long as `T` implements `HasAreaRef`. This demonstrates that the `Computer` trait itself is flexible enough to handle borrowed inputs directly, without the need for additional traits.
 
-This shows that technically, the `Computer` trait alone is sufficient to support all kinds of inputs, including borrowed inputs. However, to improve developer ergonomics, CGP also provides the `ComputerRef` trait so that the user don't need to worry about specifying `&T` in the input type parameter. Furthermore, `ComputerRef` can save us from using higher-ranked trait bounds inside where clauses and input-based delegation. As a result, it is much better suited when we want to work with borrowed inputs.
+However, to make development more ergonomic, CGP provides the `ComputerRef` trait. Using `ComputerRef` eliminates the need to explicitly write `&T` in input type parameters and avoids the complexity of higher-ranked trait bounds in where clauses or input delegation. This makes `ComputerRef` better suited for working with borrowed inputs in a clean and consistent way.
 
-CGP also provides a `PromoteRef` adapter, to convert a `Computer` provider to/from a `ComputerRef` provider. For example, the `ComputerRef` implementation for `ComputeAreaRef` is simply defined as:
+To bridge `Computer` and `ComputerRef`, CGP offers the `PromoteRef` adapter. This adapter converts a provider that implements `Computer` for borrowed inputs into a `ComputerRef` provider. For example, the `ComputerRef` implementation for `ComputeAreaRef` is defined as follows:
 
 ```rust
 delegate_components! {
@@ -1290,11 +1295,11 @@ delegate_components! {
 }
 ```
 
-What this means is that `ComputeAreaRef` implements `ComputerRef` through `PromoteRef<ComputeAreaRef>`.
+This means that `ComputeAreaRef` implements `ComputerRef` through `PromoteRef<ComputeAreaRef>`, automatically lifting its `Computer` implementation for `&T` into a `ComputerRef` implementation.
 
 ### Promotion from `Computer` to `ComputerRef`
 
-Behind the scene, `PromoteRef` is implemented as follows:
+The `PromoteRef` adapter allows a provider that implements `Computer` for borrowed inputs to become a provider that implements `ComputerRef`. Its implementation is as follows:
 
 ```rust
 #[cgp_provider]
@@ -1311,15 +1316,13 @@ where
 }
 ```
 
-In the implementation above, we can see that `PromoteRef` implements `ComputerRef` for a `Provider`, if the provider has a higher-ranked trait bound `for<'a>` that implements `Computer` for all `&'a Input` reference. Through `PromoteRef`, this higher-ranked trait bound is hidden away so that the end users don't need to be aware of their existence.
-
-It is worth noticing that `PromoteRef` also requires the inner `Computer` provider to always have the same `Output` type, regardless of which lifetime `'a` is used for the input. This has an important implication that the `ComputerRef` promotion would *not* work if the `Output` type *borrows* some value within `&'a Input`.
+Here, `PromoteRef` implements `ComputerRef` as long as the inner `Provider` supports a higher-ranked trait bound, meaning it can implement `Computer` for all lifetimes `'a` of `&'a Input`. This pattern hides the complexity of higher-ranked trait bounds, so end users do not need to think about them when using `ComputerRef`.
 
-This restriction follows from the design of `ComputerRef`, which has a singular `Output` type that is independent of the lifetime of the input reference in the `compute_ref` method. In some sense, this means that `ComputerRef` is more restrictive and cannot represent all computations that could be implemented through `Computer`. Nevertheless, in case if the user really needs to return `Output` that depends on the input lifetime, they can always revert to using `Computer` directly for such use cases.
+One important detail is that `PromoteRef` requires the inner `Computer` provider to always produce the same `Output` type for any lifetime `'a`. This means that `PromoteRef` cannot be used if the `Output` type borrows from the input reference, because `ComputerRef` defines a single `Output` type that is independent of the lifetime of the input. This limitation follows naturally from the design of `ComputerRef`. When the output must borrow from the input, the user should implement `Computer` directly instead of using `ComputerRef`.
 
 ### Promotion from `ComputerRef` to `Computer`
 
-`PromoteRef` also implements `Computer` by wrapping a `Provider` that implements `ComputerRef`, shown as follows:
+`PromoteRef` also works in the opposite direction, allowing a provider that implements `ComputerRef` to become a provider that implements `Computer` for borrowed inputs:
 
 ```rust
 #[cgp_provider]
@@ -1335,13 +1338,13 @@ where
 }
 ```
 
-Here, `PromoteRef` implements `Computer` that works with any `&Input` reference, provided that the inner `Provider` implements `ComputerRef` for `Input`.
+In this implementation, `PromoteRef` wraps a `Provider` that implements `ComputerRef` and forwards the call to `compute_ref`. As a result, it produces a `Computer` implementation that works with `&Input`.
 
-With both implementations in place, `PromoteRef` offers bidirectional transformation between `Computer` and `ComputerRef`. This allows us to easily convert the computer providers, depending on which form of interface we want to use.
+With these two implementations, `PromoteRef` provides a bidirectional bridge between `Computer` and `ComputerRef`. This flexibility allows a single provider to adapt to whichever trait is more convenient for the task, whether the interface expects `Computer` or `ComputerRef`.
 
 ## `MatchWithValueHandlersRef`
 
-To support reference-based dispatching, aside from the few reference-specific constructs that we have introduced earlier, the remaining implementation of `MatchWithValueHandlersRef` is almost as simple as the original implementation of `MatchWithValueHandlers`:
+To support reference-based dispatching in CGP, we only need to introduce a few reference-specific constructs while keeping most of the implementation very similar to the original `MatchWithValueHandlers`. The definition of `MatchWithValueHandlersRef` is shown below:
 
 ```rust
 pub type MatchWithValueHandlersRef<Provider> =
@@ -1356,11 +1359,11 @@ delegate_components! {
 }
 ```
 
-If we compare the definition with `MatchWithValueHandlers`, we could notice that the main difference is that `MatchWithFieldHandlersInputsRef` is used, in addition to some wrapping and unwrapping using `PromoteRef`.
+When comparing this definition to `MatchWithValueHandlers`, the most notable differences are the use of `MatchWithFieldHandlersInputsRef` and the additional wrapping with `PromoteRef` to facilitate conversions between `Computer` and `ComputerRef`.
 
 ### Example use of `MatchWithValueHandlersRef`
 
-To understand what is really happening, let's try to trace the computation flow of what happens when we call `MatchWithValueHandlersRef<ComputeAreaRef>` on `Shape`:
+To better understand how `MatchWithValueHandlersRef` works in practice, let us walk through what happens when we call `MatchWithValueHandlersRef<ComputeAreaRef>` on `Shape`:
 
 ```rust
 impl HasAreaRef for Shape {
@@ -1370,36 +1373,43 @@ impl HasAreaRef for Shape {
 }
 ```
 
-- The `Provider` argument to `MatchWithHandlers` is `ComputeAreaRef`. This is expanded into `HandleFieldValue<PromoteRef<ComputeAreaRef>>` when passed to `MatchWithFieldHandlersInputsRef`.
-- `UseInputDelegate` is expected to implement `ComputerRef` with `Input` being `Shape`, which is dispatched to `MatchWithFieldHandlersInputsRef`.
-- `MatchWithFieldHandlersInputsRef` is called with `Input` being `Shape`, and `Provider` being `HandleFieldValue<PromoteRef<ComputeAreaRef>>`.
-- `Shape` is expected to implement `HasFieldHandlers<HandleFieldValue<PromoteRef<ComputeAreaRef>>>`, with `Shape::Handlers` expanding into:
-    ```rust
-    Product![
-        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
-        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
-    ]
-    ```
-- The delegate entry maps to `PromoteRef<MatchWithHandlersRef<Input::Handlers>>`, which expands into:
-    ```rust
-    PromoteRef<MatchWithHandlersRef<Product![
-        ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
-        ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
-    ]>
-    ```
-- To implement `ComputerRef`, `PromoteRef<MatchWithHandlersRef<Input::Handlers>>` requires `MatchWithHandlersRef<Input::Handlers>` to implement `Computer`.
-- To implement `Computer`, `MatchWithHandlersRef<Input::Handlers>` requires `HandleFieldValue<PromoteRef<ComputeAreaRef>>` to implement the following constraints:
-    - `Computer<(), (), Field<symbol!("Circle"), &Circle>>`
-    - `Computer<(), (), Field<symbol!("Rectangle"), &Rectangle>>`
-- After unwrapping the field value by `HandleFieldValue`, the inner provider `PromoteRef<ComputeAreaRef>` needs to implement the following constraints:
-    - `Computer<(), (), &Circle>`
-    - `Computer<(), (), &Rectangle>`
-- To implement `Computer`, `PromoteRef` requires `ComputeAreaRef` to implement the following constraints:
-    - `ComputerRef<(), (), Circle>`
-    - `ComputerRef<(), (), Rectangle>`
-
-As we can see, the crucial difference in the implementation of `MatchWithValueHandlersRef` is that we need to know when is the right time to use `Computer` vs `ComputerRef`, and how to use `PromoteRef` at the right place to facilitate the conversion between `Computer` and `ComputerRef`.
-
-If this is still confusing to you, don't worry too much, as the implementation details are mostly shielded away from how it is used. The main takeaway from this exercise that aside from a few reference-specific definitions, most of the work of reference-based extensible visitor shares the same underlying implementation as the ownership-based implementation.
+The `Provider` argument to `MatchWithHandlers` is `ComputeAreaRef`, which is expanded into `HandleFieldValue<PromoteRef<ComputeAreaRef>>` when passed to `MatchWithFieldHandlersInputsRef`. The `UseInputDelegate` type is expected to implement `ComputerRef` for `Input = Shape`, and it delegates the work to `MatchWithFieldHandlersInputsRef`.
+
+Next, `MatchWithFieldHandlersInputsRef` is invoked with `Input` as `Shape` and `Provider` as `HandleFieldValue<PromoteRef<ComputeAreaRef>>`. The `Shape` type must implement `HasFieldHandlers<HandleFieldValue<PromoteRef<ComputeAreaRef>>>`, and its `Handlers` expand to:
+
+```rust
+Product![
+    ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+    ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+]
+```
+
+The delegate entry maps to `PromoteRef<MatchWithHandlersRef<Input::Handlers>>`, which becomes:
+
+```rust
+PromoteRef<MatchWithHandlersRef<Product![
+    ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+    ExtractFieldAndHandle<symbol!("Rectangle"), HandleFieldValue<PromoteRef<ComputeAreaRef>>>,
+]>>
+```
+
+In order to implement `ComputerRef`, `PromoteRef<MatchWithHandlersRef<Input::Handlers>>` requires `MatchWithHandlersRef<Input::Handlers>` to implement `Computer`. For `MatchWithHandlersRef<Input::Handlers>` to implement `Computer`, its inner provider `HandleFieldValue<PromoteRef<ComputeAreaRef>>` must satisfy the following constraints:
+
+* `Computer<(), (), Field<symbol!("Circle"), &Circle>>`
+* `Computer<(), (), Field<symbol!("Rectangle"), &Rectangle>>`
+
+After `HandleFieldValue` unwraps the actual field values, the inner provider `PromoteRef<ComputeAreaRef>` must implement:
+
+* `Computer<(), (), &Circle>`
+* `Computer<(), (), &Rectangle>`
+
+Finally, `PromoteRef` requires `ComputeAreaRef` to implement:
+
+* `ComputerRef<(), (), Circle>`
+* `ComputerRef<(), (), Rectangle>`
+
+This example shows that the key to implementing `MatchWithValueHandlersRef` lies in understanding when to use `Computer` versus `ComputerRef`, and where to apply `PromoteRef` to bridge the two. While the type expansion can appear intimidating, most of the complexity is hidden behind these abstractions.
+
+In practice, the usage of `MatchWithValueHandlersRef` feels almost identical to the ownership-based version, and the underlying implementation shares the same structure aside from a few reference-specific details.
 
 # Conclusion

From ca8e44fffe1fbc6eca730cbca2aced57c3bd6ea7 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Tue, 29 Jul 2025 21:56:47 +0000
Subject: [PATCH 16/21] Add conclusion section

---
 .../2025-07-30-extensible-datatypes-part-4.md | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/content/blog/2025-07-30-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
index ef96e88..9266508 100644
--- a/content/blog/2025-07-30-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -1413,3 +1413,94 @@ This example shows that the key to implementing `MatchWithValueHandlersRef` lies
 In practice, the usage of `MatchWithValueHandlersRef` feels almost identical to the ownership-based version, and the underlying implementation shares the same structure aside from a few reference-specific details.
 
 # Conclusion
+
+## Recap
+
+We have finally covered everything essential on the implementation details of extensible variants and extensible visitors. To give a quick recap, we started with defining the `FromVariant` trait for constructing an enum from one of its variant. We then introduced the concept of partial variants, which follows similar structure as partial records, but uses `IsVoid` to represent the absence of an extracted variant.
+
+We then introduced the `HasExtractor` trait for converting an enum into its partial variants, and the `ExtractField` trait for extracting a single variant out of the partial variants. We then learned about the remainders of a partial variants after extraction, and the use of `FinalizeExtract` to discharge a remainder once all variants in it are already extracted.
+
+We then went through the implementation of upcasting and downcasting operations for enums. We learned about the `HasFields` implementation for enums correspond to a type-level sum of fields. We then walked through the implementation of `FieldExtractor` to help extract and move the fields between a source and a target partial variants. We then juxtapose the differences between `CanUpcast` and `CanDowncast` being a matter of choosing the `HasFields` implementation from either the source or the target enum.
+
+Next, we dived deeper into the implementation of extensible visitors, starting with the implementation of `MatchWithHandlers` and `DispatchMatchers`. We then showed that `DispatchMatchers` is implemented as a monadic pipeline that short circuits on the `Ok` variant. We then explored the use of the field adapters `ExtractFieldAndHandle` and `HandleFieldValue`, and the use of `#[cgp_computer]` to convert a regular trait method into a `Computer` provider. We then learned about how `ToFieldsHandler` and `HasFieldHandlers` are used to transform the tags in the `HasFields` instance of an enum to the appropriate providers to be used with `MatchWithHandlers`. Finally, we seen how the top-level dispatcher `MatchWithValueHandlers` is implemented through type-level metaprogramming by combining all the constructs that have been covered earlier.
+
+Finally, we went through the reference-based implementation of extensible visitor, with reference-specific constructs such as the partial-ref variants and the `HasExtractorRef` trait. We then looked at the implementation of `MatchWithHandlersRef`, which uses `HasExtractorRef` to extract the borrowed variants. We learned about the use of `PromoteRef` to convert between `Computer` and `ComputerRef` providers. Finally, we have seen how `MatchWithValueHandlersRef` is implemented with a minimal difference being the use of `MatchWithHandlersRef` and `PromoteRef` to interleave between the `Computer` and `ComputerRef` implementations.
+
+## Future Work
+
+Thanks to the modular design of extensible variants, it becomes straightforward to extend further use of the pattern to support new use cases. In fact, there are a few use cases that are not yet supported by this initial version of extensible variants. While they are not technically challenging to implement, I have opted to defer the implementation to a later release, to focus on the core implementation and the writing of these blog posts.
+
+### Additional Arguments
+
+Currently, the implementation of extensible visitors do not support additional arguments to be supplied to each visitor handler. This means that we cannot support traits that require additional arguments, such as:
+
+```rust
+pub trait HasArea {
+    fn area(&self, scale_factor: f64) -> f64;
+}
+```
+
+In the above example, the `area` method requires an additional `scale_factor` argument, which should be passed through the visitor dispatcher and forwarded to the individual variant handlers.
+
+To support the pass through of additional arguments, we only need to define other kinds of adapters similar to `ExtractFieldAndHandle`, but allow the extra arguments to be bundled inside the `Input`. Similarly, we will define alternative dispatchers similar to `MatchWithValueHandlers`, but dispatches based on a bundled `Input` that contains additional arguments.
+
+### `&mut` References
+
+The current reference-based dispatch implementation is hard-coded with the use of `&` references. This means that it is currently not possible to dispatch `&mut` references to update a mutable enum. For example:
+
+```rust
+pub trait CanScale {
+    fn scale(&mut self, factor: f64);
+}
+```
+
+To support mutable references, we need to further generalize the design of partial-ref variants such that they can be *generic* over either `&` or `&mut`. This may require additional abstraction similar to `MapType`, but works on mapping the "kind" of references in the fields.
+
+### Simpler Dispatchers
+
+Extensible visitors is well suited for tackling complex use cases such as building modular interpreters. However, it is also equally well suited to solve much simpler use cases, such as simplifying the dispatch implementation of plain Rust traits such as `HasArea`.
+
+Although we have demonstrated in this article the feasibility to do so, the current ergonomic of using extensible visitors for this use case is less than idea. First, the user needs to understand how to use `#[cgp_computer]` to implement helper providers like `ComputeArea`. After that, the user still needs to manually implement `Area` for `Shape`, by calling `MatchWithValueHandlers::<ComputeArea>::compute()` with a dummy context and code.
+
+For users who are unfamiliar with CGP, the manual steps that they need to learn in order to use extensible visitors requires too much cognitive load. To streamline this use case, CGP needs to make the whole experience much more magical, so that it can be used by anyone without needing to understand CGP.
+
+A rough idea of how we could achieve this would be to introduce a proc macro that can be used to parse the plain Rust trait, such as:
+
+```rust
+#[cgp_dispatch]
+pub trait HasArea {
+    fn area(&self) -> f64;
+}
+```
+
+With a proc macro like `#[cgp_dispatch]`, we can parse the `HasArea` trait and generate the appropriate implementations that are required by extensible visitors. The interesting with this macro is that it also generates a blanket implementation for `HasArea`, so that the trait is *automatically* implemented for compatible enums like `Shape` and `ShapePlus`.
+
+The generated blanket implementation would roughly looks something like follows:
+
+```rust
+impl<Context> HasArea for Context
+where
+    Context: HasExtractorRef,
+    MatchWithValueHandlersRef<ComputeArea>: ComputerRef<(), (), Context, Output = f64>,
+{
+    fn area(&self) -> f64 {
+        MatchWithValueHandlersRef::<ComputeArea>::compute_ref(&(), PhantomData, self)
+    }
+}
+```
+
+Once we have implemented such a macro, the entire experience of using extensible visitors with simple Rust traits would hopefully become as magical as simply adding `#[cgp_dispatch]` on the relevant traits.
+
+### Custom Partial Records Updater
+
+For partial records, only a number of methods such as `TakeField` and `BuildField` that are derived with it. This makes it challenging to customize the behavior of the partial record, such as to allow overriding existing field values, filling empty fields with default values, or "taking" a default value from an empty field.
+
+To support these operations, we need to provide more generalized interfaces for interacting with the partial records, so that we don't need to derive specialized traits for each use case. An idea that I have floating around is to make use of *natural transformation* to implement generic field transformers to manipulate the fields. For example, a builder transformer is one that transforms `IsNothing` fields to `IsPresent` fields, but an overrider transformer would be one that transforms *either* `IsNothing` or `IsPresent` fields to `IsPresent` fields.
+
+## End of Series
+
+We have reached the end of the final part of this series on extensible data types. Hopefully by now, you have gained a better understanding on the design patterns that are enabled by extensible data types, and how to use them to solve real world problems.
+
+While the implementation details may be challenging to comprehend, I hope that at least it gives you a better sense of how extensible data types are implemented, and the advantages of our type-driven approach to keep the system modular and extensible.
+
+More importantly, I also hope that the articles have given you a better sense of the various design patterns used in CGP, which you can learn and apply to other areas when writing CGP code.

From f4359ad952ce9538de46ed054d3f6c4c1ebea06e Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Wed, 30 Jul 2025 09:19:19 +0000
Subject: [PATCH 17/21] AI-revise future work

---
 .../2025-07-30-extensible-datatypes-part-4.md | 82 +++++++++++--------
 1 file changed, 47 insertions(+), 35 deletions(-)

diff --git a/content/blog/2025-07-30-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
index 9266508..c4bf7ef 100644
--- a/content/blog/2025-07-30-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -1412,27 +1412,13 @@ This example shows that the key to implementing `MatchWithValueHandlersRef` lies
 
 In practice, the usage of `MatchWithValueHandlersRef` feels almost identical to the ownership-based version, and the underlying implementation shares the same structure aside from a few reference-specific details.
 
-# Conclusion
-
-## Recap
-
-We have finally covered everything essential on the implementation details of extensible variants and extensible visitors. To give a quick recap, we started with defining the `FromVariant` trait for constructing an enum from one of its variant. We then introduced the concept of partial variants, which follows similar structure as partial records, but uses `IsVoid` to represent the absence of an extracted variant.
-
-We then introduced the `HasExtractor` trait for converting an enum into its partial variants, and the `ExtractField` trait for extracting a single variant out of the partial variants. We then learned about the remainders of a partial variants after extraction, and the use of `FinalizeExtract` to discharge a remainder once all variants in it are already extracted.
-
-We then went through the implementation of upcasting and downcasting operations for enums. We learned about the `HasFields` implementation for enums correspond to a type-level sum of fields. We then walked through the implementation of `FieldExtractor` to help extract and move the fields between a source and a target partial variants. We then juxtapose the differences between `CanUpcast` and `CanDowncast` being a matter of choosing the `HasFields` implementation from either the source or the target enum.
-
-Next, we dived deeper into the implementation of extensible visitors, starting with the implementation of `MatchWithHandlers` and `DispatchMatchers`. We then showed that `DispatchMatchers` is implemented as a monadic pipeline that short circuits on the `Ok` variant. We then explored the use of the field adapters `ExtractFieldAndHandle` and `HandleFieldValue`, and the use of `#[cgp_computer]` to convert a regular trait method into a `Computer` provider. We then learned about how `ToFieldsHandler` and `HasFieldHandlers` are used to transform the tags in the `HasFields` instance of an enum to the appropriate providers to be used with `MatchWithHandlers`. Finally, we seen how the top-level dispatcher `MatchWithValueHandlers` is implemented through type-level metaprogramming by combining all the constructs that have been covered earlier.
-
-Finally, we went through the reference-based implementation of extensible visitor, with reference-specific constructs such as the partial-ref variants and the `HasExtractorRef` trait. We then looked at the implementation of `MatchWithHandlersRef`, which uses `HasExtractorRef` to extract the borrowed variants. We learned about the use of `PromoteRef` to convert between `Computer` and `ComputerRef` providers. Finally, we have seen how `MatchWithValueHandlersRef` is implemented with a minimal difference being the use of `MatchWithHandlersRef` and `PromoteRef` to interleave between the `Computer` and `ComputerRef` implementations.
+# Future Work
 
-## Future Work
+The modular design of extensible variants makes it straightforward to extend the pattern for new use cases. There are several scenarios that are not yet supported in this initial version. While none of these are technically difficult to implement, the focus for this release has been on the core functionality and the writing of these blog posts. The following areas are planned for future work.
 
-Thanks to the modular design of extensible variants, it becomes straightforward to extend further use of the pattern to support new use cases. In fact, there are a few use cases that are not yet supported by this initial version of extensible variants. While they are not technically challenging to implement, I have opted to defer the implementation to a later release, to focus on the core implementation and the writing of these blog posts.
+## Additional Arguments
 
-### Additional Arguments
-
-Currently, the implementation of extensible visitors do not support additional arguments to be supplied to each visitor handler. This means that we cannot support traits that require additional arguments, such as:
+At present, extensible visitors do not support forwarding additional arguments to individual visitor handlers. This limitation prevents traits that require extra arguments, such as:
 
 ```rust
 pub trait HasArea {
@@ -1440,13 +1426,11 @@ pub trait HasArea {
 }
 ```
 
-In the above example, the `area` method requires an additional `scale_factor` argument, which should be passed through the visitor dispatcher and forwarded to the individual variant handlers.
-
-To support the pass through of additional arguments, we only need to define other kinds of adapters similar to `ExtractFieldAndHandle`, but allow the extra arguments to be bundled inside the `Input`. Similarly, we will define alternative dispatchers similar to `MatchWithValueHandlers`, but dispatches based on a bundled `Input` that contains additional arguments.
+Here, the `area` method needs a `scale_factor` argument that must be passed through the visitor dispatcher to the variant handlers. To support this, we can create adapters similar to `ExtractFieldAndHandle` that bundle the extra arguments into the `Input`. We would then define alternative dispatchers, similar to `MatchWithValueHandlers`, which operate on these bundled inputs.
 
-### `&mut` References
+## `&mut` References
 
-The current reference-based dispatch implementation is hard-coded with the use of `&` references. This means that it is currently not possible to dispatch `&mut` references to update a mutable enum. For example:
+The current reference-based dispatch system is hard-coded to use shared references (`&`). As a result, it does not support `&mut` references for mutable operations such as:
 
 ```rust
 pub trait CanScale {
@@ -1454,17 +1438,15 @@ pub trait CanScale {
 }
 ```
 
-To support mutable references, we need to further generalize the design of partial-ref variants such that they can be *generic* over either `&` or `&mut`. This may require additional abstraction similar to `MapType`, but works on mapping the "kind" of references in the fields.
+To support mutable references, the design of partial-ref variants needs to be generalized to work with both `&` and `&mut`. This likely requires an abstraction similar to `MapType`, but for mapping the type of reference used for each field.
 
 ### Simpler Dispatchers
 
-Extensible visitors is well suited for tackling complex use cases such as building modular interpreters. However, it is also equally well suited to solve much simpler use cases, such as simplifying the dispatch implementation of plain Rust traits such as `HasArea`.
-
-Although we have demonstrated in this article the feasibility to do so, the current ergonomic of using extensible visitors for this use case is less than idea. First, the user needs to understand how to use `#[cgp_computer]` to implement helper providers like `ComputeArea`. After that, the user still needs to manually implement `Area` for `Shape`, by calling `MatchWithValueHandlers::<ComputeArea>::compute()` with a dummy context and code.
+Although extensible visitors were designed with complex use cases like modular interpreters in mind, they are equally powerful for simpler needs, such as implementing plain Rust traits like `HasArea`. While this is already possible with the current infrastructure, the ergonomics leave much to be desired.
 
-For users who are unfamiliar with CGP, the manual steps that they need to learn in order to use extensible visitors requires too much cognitive load. To streamline this use case, CGP needs to make the whole experience much more magical, so that it can be used by anyone without needing to understand CGP.
+Users must first understand and use the `#[cgp_computer]` macro to define helper providers like `ComputeArea`, and then manually implement the trait by invoking `MatchWithValueHandlers::<ComputeArea>::compute()` with dummy context and code. For those unfamiliar with CGP, these steps impose unnecessary friction and cognitive load.
 
-A rough idea of how we could achieve this would be to introduce a proc macro that can be used to parse the plain Rust trait, such as:
+To improve usability, CGP could offer a procedural macro to automate this boilerplate. For instance, a trait could be annotated as follows:
 
 ```rust
 #[cgp_dispatch]
@@ -1473,9 +1455,9 @@ pub trait HasArea {
 }
 ```
 
-With a proc macro like `#[cgp_dispatch]`, we can parse the `HasArea` trait and generate the appropriate implementations that are required by extensible visitors. The interesting with this macro is that it also generates a blanket implementation for `HasArea`, so that the trait is *automatically* implemented for compatible enums like `Shape` and `ShapePlus`.
+The `#[cgp_dispatch]` macro would parse the trait definition and generate the necessary code to integrate it with the extensible visitor framework. This includes generating a blanket implementation for the trait so that it is automatically implemented for compatible enums like `Shape` or `ShapePlus`.
 
-The generated blanket implementation would roughly looks something like follows:
+The generated implementation would resemble:
 
 ```rust
 impl<Context> HasArea for Context
@@ -1489,13 +1471,33 @@ where
 }
 ```
 
-Once we have implemented such a macro, the entire experience of using extensible visitors with simple Rust traits would hopefully become as magical as simply adding `#[cgp_dispatch]` on the relevant traits.
+With such a macro in place, using extensible visitors to implement common traits would become as easy as annotating the trait with `#[cgp_dispatch]`, removing the need to understand the inner workings of CGP for simple use cases.
+
+## Custom Partial Records Updater
+
+Currently, partial records only support a small set of operations like `TakeField` and `BuildField`. This makes it difficult to customize behavior, such as overriding existing field values, filling empty fields with defaults, or taking a default value from an empty field.
+
+To support these scenarios, more generalized interfaces for interacting with partial records are needed. A promising approach is to use *natural transformations* to implement generic field transformers. For example, a builder transformer would convert `IsNothing` fields into `IsPresent` fields, while an overrider transformer would convert either `IsNothing` or `IsPresent` fields into `IsPresent` fields. This would allow for flexible and reusable field manipulation strategies.
 
-### Custom Partial Records Updater
+## Explanation for Computation Hierarchy
 
-For partial records, only a number of methods such as `TakeField` and `BuildField` that are derived with it. This makes it challenging to customize the behavior of the partial record, such as to allow overriding existing field values, filling empty fields with default values, or "taking" a default value from an empty field.
+Aside from `Computer`, `ComputerRef`, and `Handler`, CGP also defines various other traits for different kinds of computations. For example, there is `TryComputer` for running computation that may fail, and `TryComputerRef` for fallable computation with reference input.
 
-To support these operations, we need to provide more generalized interfaces for interacting with the partial records, so that we don't need to derive specialized traits for each use case. An idea that I have floating around is to make use of *natural transformation* to implement generic field transformers to manipulate the fields. For example, a builder transformer is one that transforms `IsNothing` fields to `IsPresent` fields, but an overrider transformer would be one that transforms *either* `IsNothing` or `IsPresent` fields to `IsPresent` fields.
+There are various constructs, such as `Promote`, that can be used to convert between different kinds of computation providers. There are also multiple ways to compose these computation providers, such as using `PipeHandlers` or `PipeMonadic`. In particular, the monadic composition of these handlers would require delicate explanation, especially since the monad implementation in CGP is not quite the same as the monads in Haskell.
+
+Originally, I had a plan for a *fifth* part of the series to explain the hierarchy for the computation traits in CGP. However, I have decided to split it out to its own dedicated blog post (or series), since I have already written too much just to cover the topic of extensible data types.
+
+# Conclusion
+
+We have now reached the end of our deep dive into the implementation details of extensible variants and extensible visitors. To recap the journey, we began by defining the `FromVariant` trait, which allows constructing an enum from one of its variants. We then introduced partial variants, which mirror the structure of partial records but use `IsVoid` to indicate the absence of a variant.
+
+From there, we defined the `HasExtractor` trait to convert an enum into its partial variants, followed by the `ExtractField` trait to extract a single variant from those partial variants. This led to the concept of remainders, representing what is left after a variant is extracted, and the `FinalizeExtract` trait, which finalizes a remainder once all its variants have been handled.
+
+We then examined how upcasting and downcasting for enums are implemented. We explored how the `HasFields` implementation for enums represents a type-level sum of fields and how `FieldExtractor` is used to move fields between source and target partial variants. The difference between `CanUpcast` and `CanDowncast` boils down to choosing whether the `HasFields` implementation comes from the source or the target enum.
+
+Next, we delved into the implementation of extensible visitors, beginning with `MatchWithHandlers` and `DispatchMatchers`. We saw that `DispatchMatchers` is structured as a monadic pipeline that short-circuits when it encounters an `Ok` value. We examined the role of field adapters like `ExtractFieldAndHandle` and `HandleFieldValue`, and we explored how `#[cgp_computer]` transforms a regular trait method into a `Computer` provider. We then discussed how `ToFieldsHandler` and `HasFieldHandlers` convert the tags in an enum’s `HasFields` implementation into the appropriate providers for use with `MatchWithHandlers`. Finally, we looked at how the top-level dispatcher `MatchWithValueHandlers` is assembled through type-level metaprogramming, combining all the earlier components into a cohesive system.
+
+We concluded with the reference-based implementation of extensible visitors. This introduced reference-specific constructs such as partial-ref variants and the `HasExtractorRef` trait. We examined how `MatchWithHandlersRef` uses `HasExtractorRef` to extract borrowed variants, and how `PromoteRef` bridges between `Computer` and `ComputerRef` providers. We then saw that `MatchWithValueHandlersRef` is implemented with only minimal differences from its ownership-based counterpart, relying on `MatchWithHandlersRef` and `PromoteRef` to interleave `Computer` and `ComputerRef` computations.
 
 ## End of Series
 
@@ -1504,3 +1506,13 @@ We have reached the end of the final part of this series on extensible data type
 While the implementation details may be challenging to comprehend, I hope that at least it gives you a better sense of how extensible data types are implemented, and the advantages of our type-driven approach to keep the system modular and extensible.
 
 More importantly, I also hope that the articles have given you a better sense of the various design patterns used in CGP, which you can learn and apply to other areas when writing CGP code.
+
+The design and implementation of extensible data types stretches the limits of how CGP can be used to implement advanced language features that typically need to be implemented natively within the Rust compiler itself. However, in everyday development, you are likely not going to need to use all the advanced programming techniques that have been presented in this series.
+
+I hope that even if you don't understand everything that have been presented here, it would still given you sufficient motivation to start learning CGP *from the basics*. The key points that I hope you'd understand is that even if many of the basic CGP patterns may seem "superflous" or "overengineered" while you learn them, they are very essential in enabling the kinds of advanced design patterns that we have seen in extensible data types and [Hypershell](/blog/hypershell-release/).
+
+Furthermore, you don't need to build entirely new language features or DSLs for CGP to be useful. As we will see in future blog posts, there are many more basic and intermediary CGP patterns that can be used to help you build practical applications. Thank you for your support and attention given to the CGP project, and stay tuned for more exciting development coming to CGP!
+
+## Hire Me
+
+P.S. Btw, [I am available for hire](/hire)!

From fa715dbe8f168ba36f412ba52da85d6ae9ebeb2d Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Wed, 30 Jul 2025 09:26:18 +0000
Subject: [PATCH 18/21] AI-revise conclusion

---
 .../2025-07-30-extensible-datatypes-part-4.md  | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/content/blog/2025-07-30-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
index c4bf7ef..2489e14 100644
--- a/content/blog/2025-07-30-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -1481,11 +1481,11 @@ To support these scenarios, more generalized interfaces for interacting with par
 
 ## Explanation for Computation Hierarchy
 
-Aside from `Computer`, `ComputerRef`, and `Handler`, CGP also defines various other traits for different kinds of computations. For example, there is `TryComputer` for running computation that may fail, and `TryComputerRef` for fallable computation with reference input.
+Beyond `Computer`, `ComputerRef`, and `Handler`, CGP defines several other traits that represent different kinds of computations. For example, `TryComputer` supports computations that may fail, and `TryComputerRef` handles fallible computations that operate on reference inputs. These traits make it possible to model a wide range of behaviors, from straightforward value computations to error-aware or reference-based processing.
 
-There are various constructs, such as `Promote`, that can be used to convert between different kinds of computation providers. There are also multiple ways to compose these computation providers, such as using `PipeHandlers` or `PipeMonadic`. In particular, the monadic composition of these handlers would require delicate explanation, especially since the monad implementation in CGP is not quite the same as the monads in Haskell.
+CGP also provides constructs such as `Promote` that allow seamless conversion between different types of computation providers. In addition, it supports multiple ways to compose these providers. Two notable examples are `PipeHandlers` and `PipeMonadic`. Monadic composition in particular requires delicate explanation, because CGP’s approach to monads does not behave exactly like the familiar monads in Haskell. Understanding how these monadic pipelines operate is essential for developers who want to create more sophisticated and composable computation flows.
 
-Originally, I had a plan for a *fifth* part of the series to explain the hierarchy for the computation traits in CGP. However, I have decided to split it out to its own dedicated blog post (or series), since I have already written too much just to cover the topic of extensible data types.
+My original plan was to dedicate a fifth part of this series to explain the complete hierarchy of computation traits in CGP. However, the implementation details for extensible data types have already required extensive coverage, and attempting to include computation hierarchy in the same series would make it overwhelming. As a result, I have decided to split that discussion into its own dedicated post, or potentially a separate series, to provide the depth and clarity it deserves.
 
 # Conclusion
 
@@ -1501,17 +1501,17 @@ We concluded with the reference-based implementation of extensible visitors. Thi
 
 ## End of Series
 
-We have reached the end of the final part of this series on extensible data types. Hopefully by now, you have gained a better understanding on the design patterns that are enabled by extensible data types, and how to use them to solve real world problems.
+We have reached the conclusion of this series on extensible data types. By now, you should have a clearer understanding of the design patterns that extensible data types make possible and how they can be applied to solve real-world problems in Rust.
 
-While the implementation details may be challenging to comprehend, I hope that at least it gives you a better sense of how extensible data types are implemented, and the advantages of our type-driven approach to keep the system modular and extensible.
+Although some of the implementation details can be challenging, I hope this series has given you a solid sense of how extensible data types are structured, and why a type-driven approach allows your system to remain both modular and flexible as it grows.
 
-More importantly, I also hope that the articles have given you a better sense of the various design patterns used in CGP, which you can learn and apply to other areas when writing CGP code.
+More importantly, I hope these articles have helped you recognize the design patterns that underpin CGP. Learning to identify and apply these patterns will make your own CGP code more effective and give you tools you can use well beyond this particular topic.
 
-The design and implementation of extensible data types stretches the limits of how CGP can be used to implement advanced language features that typically need to be implemented natively within the Rust compiler itself. However, in everyday development, you are likely not going to need to use all the advanced programming techniques that have been presented in this series.
+The design and implementation of extensible data types push the boundaries of what CGP can achieve. They show how CGP can model advanced language features that are often only possible through direct integration into the Rust compiler. In day-to-day development, you may not need to reach for every advanced technique demonstrated here, but understanding that these patterns exist will broaden your perspective on what is possible.
 
-I hope that even if you don't understand everything that have been presented here, it would still given you sufficient motivation to start learning CGP *from the basics*. The key points that I hope you'd understand is that even if many of the basic CGP patterns may seem "superflous" or "overengineered" while you learn them, they are very essential in enabling the kinds of advanced design patterns that we have seen in extensible data types and [Hypershell](/blog/hypershell-release/).
+Even if you have not fully absorbed every concept presented, I hope this series has inspired you to begin learning CGP from the fundamentals. It is important to realize that many of the basic CGP patterns may initially seem unnecessary or overengineered, yet they are the foundation that makes advanced patterns like extensible data types and [Hypershell](/blog/hypershell-release/) achievable.
 
-Furthermore, you don't need to build entirely new language features or DSLs for CGP to be useful. As we will see in future blog posts, there are many more basic and intermediary CGP patterns that can be used to help you build practical applications. Thank you for your support and attention given to the CGP project, and stay tuned for more exciting development coming to CGP!
+Finally, you do not need to create entirely new language features or DSLs for CGP to prove valuable. In upcoming posts, we will explore more foundational and intermediate CGP patterns that can help you build practical and maintainable Rust applications. Thank you for following this series and for your support of the CGP project. Exciting developments are on the horizon, and I look forward to sharing them with you.
 
 ## Hire Me
 

From 45a3c2f7d4765113fc9f34345b8e0291f4e30154 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Wed, 30 Jul 2025 09:35:33 +0000
Subject: [PATCH 19/21] Add recap

---
 .../2025-07-30-extensible-datatypes-part-4.md     | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/content/blog/2025-07-30-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
index 2489e14..048e93f 100644
--- a/content/blog/2025-07-30-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -8,6 +8,21 @@ authors = ["Soares Chen"]
 
 +++
 
+# Recap
+
+This is the **fourth** part of the blog series on **Programming Extensible Data Types in Rust with CGP**. You can read the [first](/blog/extensible-datatypes-part-1), [second](/blog/extensible-datatypes-part-2) and [third](/blog/extensible-datatypes-part-3) parts here.
+
+As a recap, we have covered the new release of [**CGP v0.4.2**](https://github.com/contextgeneric/cgp/releases/tag/v0.4.2) which now supports the use of **extensible records and variants**, allowing developers to write code that operates on *any struct containing specific fields* or *any enum containing specific variants*, without needing their concrete definition.
+
+In the first part of the series, [**Modular App Construction and Extensible Builders**](/blog/extensible-datatypes-part-1), we demonstrated an example use of the **extensible builder pattern**, which uses **extensible records** to support modular construction of an application context.
+
+Similarly, in the second part of the series, [**Modular Interpreters and Extensible Visitors**](/blog/extensible-datatypes-part-2), we saw how the modular visitor pattern allows us to implement evaluation and to-Lisp conversion for each variant of a language expression enum using separate visitor providers.
+
+In the third part of the series, [**Implementing Extensible Records**](/blog/extensible-datatypes-part-3), we have walked through the internal implementation of extensible records, and learned about concepts such as partial records and builder dispatchers.
+
+In this final fourth part of the series, we will have the same walk through for the internal implementation details for **extensible variants**.
+
+---
 
 # Implementation of Extensible Variants
 

From f7a9a4f1487be0948ff36f35ee525385112de61f Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Wed, 30 Jul 2025 09:40:25 +0000
Subject: [PATCH 20/21] Update links

---
 content/_index.md                                      | 1 +
 content/blog/2025-07-07-extensible-datatypes-part-1.md | 2 +-
 content/blog/2025-07-09-extensible-datatypes-part-2.md | 2 +-
 content/blog/2025-07-12-extensible-datatypes-part-3.md | 2 +-
 content/blog/2025-07-30-extensible-datatypes-part-4.md | 4 ++++
 themes/juice/templates/index.html                      | 4 ++--
 6 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/content/_index.md b/content/_index.md
index 54d1db2..a6be3c1 100644
--- a/content/_index.md
+++ b/content/_index.md
@@ -17,6 +17,7 @@ Read the blog series **Programming Extensible Data Types in Rust with CGP** for
 - [**Part 1: Modular App Construction and Extensible Builders**](/blog/extensible-datatypes-part-1/)
 - [**Part 2: Modular Interpreters and Extensible Visitors**](/blog/extensible-datatypes-part-2/)
 - [**Part 3: Implementing Extensible Records**](/blog/extensible-datatypes-part-3/)
+- [**Part 4: Implementing Extensible Variants**](/blog/extensible-datatypes-part-4/)
 
 # Overview
 
diff --git a/content/blog/2025-07-07-extensible-datatypes-part-1.md b/content/blog/2025-07-07-extensible-datatypes-part-1.md
index cabbd65..83ab945 100644
--- a/content/blog/2025-07-07-extensible-datatypes-part-1.md
+++ b/content/blog/2025-07-07-extensible-datatypes-part-1.md
@@ -38,7 +38,7 @@ This article is the first in a five-part series exploring the examples and imple
 
 [**Part 3: Implementing Extensible Records**](/blog/extensible-datatypes-part-3) – Here, we walk through the internal mechanics behind extensible records. We show how CGP supports the modular builder pattern demonstrated in Part 1 through its underlying type and trait machinery.
 
-**Part 4: Implementing Extensible Variants** – This part mirrors Part 3, but for extensible variants. We examine how extensible variants are implemented, and compare the differences and similarities between extensible records and variants.
+[**Part 4: Implementing Extensible Variants**](/blog/extensible-datatypes-part-4) – This part mirrors Part 3, but for extensible variants. We examine how extensible variants are implemented, and compare the differences and similarities between extensible records and variants.
 
 # Feature Highlighs
 
diff --git a/content/blog/2025-07-09-extensible-datatypes-part-2.md b/content/blog/2025-07-09-extensible-datatypes-part-2.md
index 1dcb2a0..5a3c79f 100644
--- a/content/blog/2025-07-09-extensible-datatypes-part-2.md
+++ b/content/blog/2025-07-09-extensible-datatypes-part-2.md
@@ -26,7 +26,7 @@ In this second part of the series, we will explore the use of **extensible varia
 
 [**Part 3: Implementing Extensible Records**](/blog/extensible-datatypes-part-3) – Here, we walk through the internal mechanics behind extensible records. We show how CGP supports the modular builder pattern demonstrated in Part 1 through its underlying type and trait machinery.
 
-**Part 4: Implementing Extensible Variants** – This part mirrors Part 3, but for extensible variants. We examine how extensible variants are implemented, and compare the differences and similarities between extensible records and variants.
+[**Part 4: Implementing Extensible Variants**](/blog/extensible-datatypes-part-4) – This part mirrors Part 3, but for extensible variants. We examine how extensible variants are implemented, and compare the differences and similarities between extensible records and variants.
 
 # Extending the Visitor Pattern
 
diff --git a/content/blog/2025-07-12-extensible-datatypes-part-3.md b/content/blog/2025-07-12-extensible-datatypes-part-3.md
index 19f730a..2f30a57 100644
--- a/content/blog/2025-07-12-extensible-datatypes-part-3.md
+++ b/content/blog/2025-07-12-extensible-datatypes-part-3.md
@@ -34,7 +34,7 @@ We will first explore the implementation of extensible records in this part, fol
 
 **Part 3: Implementing Extensible Records** (this post) – Here, we walk through the internal mechanics behind extensible records. We show how CGP supports the modular builder pattern demonstrated in Part 1 through its underlying type and trait machinery.
 
-**Part 4: Implementing Extensible Variants** – This part mirrors Part 3, but for extensible variants. We examine how extensible variants are implemented, and compare the differences and similarities between extensible records and variants.
+[**Part 4: Implementing Extensible Variants**](/blog/extensible-datatypes-part-4) – This part mirrors Part 3, but for extensible variants. We examine how extensible variants are implemented, and compare the differences and similarities between extensible records and variants.
 
 # Underlying Theory
 
diff --git a/content/blog/2025-07-30-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
index 048e93f..caf7f30 100644
--- a/content/blog/2025-07-30-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -1427,6 +1427,8 @@ This example shows that the key to implementing `MatchWithValueHandlersRef` lies
 
 In practice, the usage of `MatchWithValueHandlersRef` feels almost identical to the ownership-based version, and the underlying implementation shares the same structure aside from a few reference-specific details.
 
+---
+
 # Future Work
 
 The modular design of extensible variants makes it straightforward to extend the pattern for new use cases. There are several scenarios that are not yet supported in this initial version. While none of these are technically difficult to implement, the focus for this release has been on the core functionality and the writing of these blog posts. The following areas are planned for future work.
@@ -1502,6 +1504,8 @@ CGP also provides constructs such as `Promote` that allow seamless conversion be
 
 My original plan was to dedicate a fifth part of this series to explain the complete hierarchy of computation traits in CGP. However, the implementation details for extensible data types have already required extensive coverage, and attempting to include computation hierarchy in the same series would make it overwhelming. As a result, I have decided to split that discussion into its own dedicated post, or potentially a separate series, to provide the depth and clarity it deserves.
 
+---
+
 # Conclusion
 
 We have now reached the end of our deep dive into the implementation details of extensible variants and extensible visitors. To recap the journey, we began by defining the `FromVariant` trait, which allows constructing an enum from one of its variants. We then introduced partial variants, which mirror the structure of partial records but use `IsVoid` to indicate the absence of a variant.
diff --git a/themes/juice/templates/index.html b/themes/juice/templates/index.html
index 3f4a618..74e0874 100644
--- a/themes/juice/templates/index.html
+++ b/themes/juice/templates/index.html
@@ -6,10 +6,10 @@
     <meta charset="UTF-8">
     <title>{% block title %}{{ config.title }}{% endblock title %}</title>
     <meta property="og:title" content="{% block titlefb %}{{ config.title }}{% endblock titlefb %}" />
-    <meta property="og:description" content="{% block descfb %}Context-Generic Programming: Modular programming paradim for Rust{% endblock descfb %}">
+    <meta property="og:description" content="{% block descfb %}Context-Generic Programming: Modular programming paradigm for Rust{% endblock descfb %}">
     <meta property="og:image" content="https://contextgeneric.dev/cgp-logo.png">
     <meta name="twitter:title" content="{% block titletw %}{{ config.title }}{% endblock titletw %}">
-    <meta name="twitter:description" content="{% block desctw %}Context-Generic Programming: Modular programming paradim for Rust{% endblock desctw %}">
+    <meta name="twitter:description" content="{% block desctw %}Context-Generic Programming: Modular programming paradigm for Rust{% endblock desctw %}">
     <meta name="twitter:image" content="https://contextgeneric.dev/cgp-logo.png">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     {% block favicon %}

From 73e911d25dd85d3201a415a663d3b131f59d4332 Mon Sep 17 00:00:00 2001
From: Soares Chen <soares.chen@maybevoid.com>
Date: Wed, 30 Jul 2025 10:16:12 +0000
Subject: [PATCH 21/21] Ready for publish

---
 .../2025-07-30-extensible-datatypes-part-4.md  | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/content/blog/2025-07-30-extensible-datatypes-part-4.md b/content/blog/2025-07-30-extensible-datatypes-part-4.md
index caf7f30..f0adbc5 100644
--- a/content/blog/2025-07-30-extensible-datatypes-part-4.md
+++ b/content/blog/2025-07-30-extensible-datatypes-part-4.md
@@ -2,8 +2,6 @@
 
 title = "Programming Extensible Data Types in Rust with CGP - Part 4: Implementing Extensible Variants"
 
-description = ""
-
 authors = ["Soares Chen"]
 
 +++
@@ -243,7 +241,8 @@ In this example, we begin by converting the `Shape` value into a `PartialShape`
 By the time we reach the second `Err` case, the remainder has the type `PartialShape<IsVoid, IsVoid>`, which cannot contain any valid variant. Because of this, we can safely omit any further pattern matching, and the compiler guarantees that there are no unreachable or unhandled cases.
 
 What makes this approach so powerful is that the Rust type system can statically verify that it is impossible to construct a valid value for `PartialShape<IsVoid, IsVoid>`. We no longer need to write boilerplate `_ => unreachable!()` code or use runtime assertions. The type system ensures exhaustiveness and soundness entirely at compile time, enabling safer and more maintainable implementation of extensible variants.
-## Short Circuiting Remainder
+
+## Short-Circuiting Remainder
 
 In our earlier implementation of `compute_area`, we used nested `match` expressions to handle the `Result` returned from each call to `extract_field`. If you are familiar with the `?` operator in Rust, you might be wondering why we didn’t use it here to simplify the logic.
 
@@ -690,7 +689,7 @@ fn rectangle_area(rectangle: Rectangle) -> f64 {
 
 ### `#[cgp_computer]` Macro
 
-The `#[cgp_computer]` macro allows us to transform these pure functions into context-generic providers that can be referenced as types. Behind the scenes, this macro generates a `Computer` implementation similar to the following:
+The `#[cgp_computer]` macro allows us to transform these pure functions into context-generic providers that can be referenced as types. Behind the scenes, this macro generates `Computer` implementations similar to the following:
 
 ```rust
 #[cgp_provider]
@@ -703,7 +702,7 @@ impl<Context, Code> Computer<Context, Code, Circle> for CircleArea {
 }
 ```
 
-This macro simplifies the process of defining `Computer` instances by letting us write them as plain functions. Because the macro ignores the `Context` and `Code` types, the generated provider works with any `Context` and `Code` you supply.
+This macro simplifies the process of defining `Computer` providers by letting us write them as plain functions. Because the macro ignores the `Context` and `Code` types, the generated provider works with any `Context` and `Code` you supply.
 
 ### `ComputeShapeArea` Handler
 
@@ -1008,8 +1007,7 @@ Under the hood, this type alias resolves to `MatchWithHandlers` through the foll
 
 * `MatchWithValueHandlers<ComputeArea>` expands to `UseInputDelegate<MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>>`.
 * When the `Input` is `Shape`, `UseInputDelegate` locates the corresponding `DelegateComponent` mapping for `Shape` in `MatchWithFieldHandlersInputs<HandleFieldValue<ComputeArea>>`.
-* That mapping exists because `Shape` implements `HasFieldHandlers<HandleFieldValue<ComputeArea>>`.
-* As we saw earlier, this expands to:
+* That mapping exists because `Shape` implements `HasFieldHandlers<HandleFieldValue<ComputeArea>>`. As we saw earlier, this expands to:
   ```rust
   Product![
       ExtractFieldAndHandle<symbol!("Circle"), HandleFieldValue<ComputeArea>>,
@@ -1042,7 +1040,7 @@ impl HasArea for ShapePlus {
 }
 ```
 
-Although some boilerplate still remains, this approach is significantly simpler than manually matching each variant or relying on procedural macros. It also brings more flexibility and type safety. In the future, CGP may provide more ergonomic layers on top of this pattern, making common use cases like `HasArea` even easier to express.
+Although some boilerplate still remains, this approach is significantly simpler than manually matching each variant or relying on procedural macros. It also brings more flexibility and type safety. In the future, CGP may provide more ergonomic abstractions on top of this pattern, making common use cases like `HasArea` even easier to express.
 
 ## Dispatching to Context
 
@@ -1502,7 +1500,7 @@ Beyond `Computer`, `ComputerRef`, and `Handler`, CGP defines several other trait
 
 CGP also provides constructs such as `Promote` that allow seamless conversion between different types of computation providers. In addition, it supports multiple ways to compose these providers. Two notable examples are `PipeHandlers` and `PipeMonadic`. Monadic composition in particular requires delicate explanation, because CGP’s approach to monads does not behave exactly like the familiar monads in Haskell. Understanding how these monadic pipelines operate is essential for developers who want to create more sophisticated and composable computation flows.
 
-My original plan was to dedicate a fifth part of this series to explain the complete hierarchy of computation traits in CGP. However, the implementation details for extensible data types have already required extensive coverage, and attempting to include computation hierarchy in the same series would make it overwhelming. As a result, I have decided to split that discussion into its own dedicated post, or potentially a separate series, to provide the depth and clarity it deserves.
+My original plan was to dedicate a fifth part of this series to explain the complete hierarchy of computation traits in CGP. However, the implementation details for extensible data types have already required extensive coverage, and attempting to include computation hierarchy in the same series would make it overwhelming. As a result, I have decided to split that explanation into its own dedicated post, or potentially a separate series, to provide the depth and clarity it deserves.
 
 ---
 
@@ -1522,7 +1520,7 @@ We concluded with the reference-based implementation of extensible visitors. Thi
 
 We have reached the conclusion of this series on extensible data types. By now, you should have a clearer understanding of the design patterns that extensible data types make possible and how they can be applied to solve real-world problems in Rust.
 
-Although some of the implementation details can be challenging, I hope this series has given you a solid sense of how extensible data types are structured, and why a type-driven approach allows your system to remain both modular and flexible as it grows.
+Although some of the implementation details can be challenging, I hope this series has given you a solid sense of how extensible data types are structured, and why a type-driven approach allows our system to remain both modular and flexible as it grows.
 
 More importantly, I hope these articles have helped you recognize the design patterns that underpin CGP. Learning to identify and apply these patterns will make your own CGP code more effective and give you tools you can use well beyond this particular topic.
 
