You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/blog/2025-10-12-v0.5.0-release.md
+13-11Lines changed: 13 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -104,7 +104,7 @@ With this setup, `HasArea` is now automatically implemented for `Shape` without
104
104
105
105
Behind the scenes, the `#[cgp_auto_dispatch]` macro generates a blanket implementation of `HasArea` using [extensible visitors](/blog/extensible-datatypes-part-4/) to dispatch calls to the appropriate variant. Because `Shape` uses `#[derive(CgpData)]`, it already includes the extensible variant constructs needed for the blanket implementation.
106
106
107
-
An important detail is that `#[derive(CgpData)]` and `#[cgp_auto_dispatch]` work seamlessly across crate boundaries. The trait and the enum do not need to know about each other for the automatic implementation to take effect. Everything just works.
107
+
An important detail is that `#[derive(CgpData)]` and `#[cgp_auto_dispatch]` work seamlessly across crate boundaries. The trait and the enum do not need to know about each other for the blanket implementation to take effect. Everything just works.
108
108
109
109
A deeper explanation of how `#[cgp_auto_dispatch]` operates will be covered in a future blog post. For now, you can experiment with it directly in your projects. Even if you are not yet using the rest of CGP, this macro can simplify your code right away.
The `UpdateField` trait allows you to replace a *wrapped* value within a partial record with another wrapped value. It is automatically derived by `#[CgpData]`, which means that field update operations can rely on `UpdateField` without requiring any manual derivation.
132
+
The `UpdateField` trait allows you to replace a *wrapped* value within a partial record with another wrapped value. It is automatically derived by `#[derive(CgpData)]`, which means that field update operations can rely on `UpdateField` without requiring any manual derivation.
133
133
134
134
For instance, the [`BuildField`](/blog/extensible-datatypes-part-3/#buildfield-trait) trait is now implemented as a blanket implementation that builds upon `UpdateField`:
In this example, the `ContainsValue` trait matches on an `Output` type and yields its underlying `Value` type. The `ErrMonadic` provider implements `ContainsValue` by extracting the value type `T` from a `Result<T, E>`.
308
308
309
-
Instead of relying on the standard monadic `bind` operation, CGP introduces a *lifting* mechanism that elevates the input of a `Computer` provider:
309
+
Instead of relying on the standard monadic `bind` operation, CGP introduces a *lifting* mechanism that wraps the input of a `Computer` provider:
310
310
311
311
```rust
312
312
impl<Context, Code, T1, T2, E, M, Cont> Computer<Context, Code, Result<T1, E>> forBindErr<M, Cont>
@@ -330,9 +330,9 @@ which reverses the argument order of the standard bind operator `(>>=)`:
330
330
(>>=) ::ma-> (a->mb) ->mb
331
331
```
332
332
333
-
By reversing the argument order, CGP effectively turns the bind operator into a higher-order function that *lifts* monadic functions, allowing them to be composed through ordinary function composition.
333
+
By reversing the argument order, CGP effectively turns the bind operator into a higher-order function that *lifts* monadic functions, allowing them to be composed afterward through ordinary function composition.
334
334
335
-
At present, CGP’s monadic implementation requires separate bind implementations for synchronous and asynchronous computations. This limitation exists because stable Rust does not yet support [impl trait in type aliases](https://rust-lang.github.io/impl-trait-initiative/explainer/tait.html), which prevents us to name anonymous `impl Future` as the `Output` type in `Computer`. Once this feature becomes stable, CGP will be able to extend monadic support to opaque types that implement`impl Trait`, including `Future`, `Stream`, and `Iterator`.
335
+
At present, CGP’s monadic implementation requires separate bind implementations for synchronous and asynchronous computations. This limitation exists because stable Rust does not yet support [impl trait in type aliases](https://rust-lang.github.io/impl-trait-initiative/explainer/tait.html), which prevents us to name anonymous `impl Future` as the `Output` type in `Computer`. Once this feature becomes stable, CGP will be able to extend monadic support to opaque types behind`impl Trait`, including `Future`, `Stream`, and `Iterator`.
336
336
337
337
A complete introduction to monadic computation within CGP deserves its own dedicated article. For now, the key takeaway is that CGP v0.5.0 establishes a foundational monadic layer that enables the development of more advanced abstractions, such as those seen in extensible visitors, and paves the way for future extensions of the framework.
The value `3` in the first position represents the length of the symbol. This metadata is generated by the `Symbol!` macro starting from v0.5.0, enabling the reconstruction of the corresponding `&'static str` value during const evaluation.
370
370
371
-
This approach is necessary because, without it, full const generics support would be required to “count” the number of characters in a type. By precomputing the length inside the `Symbol!` macro, it becomes possible to construct an array of the correct size and iterate within const evaluation to recreate the string.
371
+
This approach is necessary because, without it, full const generics support would be required to “count” the number of characters in a type, which is not yet available in stable Rust. By precomputing the length inside the `Symbol!` macro, it becomes possible to construct an array of the correct size and iterate within const evaluation to recreate the string value.
372
372
373
-
The ability to produce static strings is particularly valuable in generic code that needs to pass `&str` values to other functions, such as in [`cgp-serde`](https://github.com/contextgeneric/cgp-serde/blob/d7a08cf99f3a741e6eb16ae58f61af5fb5e5253c/crates/cgp-serde/src/providers/fields.rs#L33-L59). Without this feature, a new `String` would have to be reallocated every time a function is called, which would significantly reduce the performance benefits of using symbols as string values.
373
+
The ability to produce static strings is particularly valuable in generic code that needs to pass `&str` values to other functions, such as in [`cgp-serde`](https://github.com/contextgeneric/cgp-serde/blob/d7a08cf99f3a741e6eb16ae58f61af5fb5e5253c/crates/cgp-serde/src/providers/fields.rs#L33-L59). Without this feature, a new `String`value would have to be reconstructed every time a function is called, which would significantly impact the performance of using symbols as string values.
374
374
375
375
---
376
376
@@ -389,7 +389,9 @@ impl<T: Send + Sync> Async for T {}
389
389
390
390
In earlier versions of CGP, the `Async` trait served as an alias for `Send + Sync`. It was used in trait bounds for abstract types and generic parameters to ensure that the `Future` returned by generic async functions could implement `Send`. This requirement is common in functions such as [`tokio::spawn`](https://docs.rs/tokio/latest/tokio/task/fn.spawn.html), and it has traditionally been one of the main reasons Rust developers annotate `Send + Sync` throughout their code.
391
391
392
-
While this approach ensured correctness, it also introduced considerable boilerplate. CGP’s codebase relied heavily on `Async` bounds, which made it cumbersome to support both synchronous and asynchronous use cases. For example, prior to v0.5.0, traits such as `HasAsyncErrorType` acted as aliases for `HasErrorType<Error: Async> + Async`. Maintaining both async and sync versions of similar traits was necessary so that non-async users could instantiate abstract types like `Error` with values that were not `Send`.
392
+
In order to support Send-able futures, CGP code used to be full of boilerplate of the use of Async. It also makes it more complicated to support both async and sync use cases in CGP.
393
+
394
+
Prior to v0.5.0, CGP’s codebase relied heavily on `Async` bounds to helps ensure that generic async functions can be called within `tokio::spawn`, but it also introduced considerable boilerplate. It also made it cumbersome for CGP to support both synchronous and asynchronous use cases, requiring traits such as `HasAsyncErrorType` to act as aliases for `HasErrorType<Error: Async> + Async`. Maintaining both async and sync versions of similar traits was necessary so that non-async users could instantiate abstract types like `Error` with values that were not `Send`.
393
395
394
396
To improve ergonomics around `Send`-able futures, the Rust compiler team has been developing [Return Type Notation (RTN)](https://github.com/rust-lang/rust/issues/109417). RTN will allow developers to impose the `Send` bound on a `Future` retroactively, at the point where it is actually needed, such as when passing it to `tokio::spawn`. With RTN, async code using CGP could avoid the need for pervasive `Send + Sync` annotations.
395
397
@@ -465,17 +467,17 @@ impl SendRunner<App> for AppComponents {
465
467
466
468
By directly implementing `SendRunner` for `App`, the trait system can access the concrete type and its associated types, allowing it to retroactively determine that the future returned by `run` implements `Send`. This effectively emulates RTN by implementing the proxy trait at the top level.
467
469
468
-
Using this approach, CGP can remove all uses of `Async` without waiting for RTN to stabilize. Meanwhile, users who need `Send`-bound futures can rely on the proxy trait technique to recover the `Send` bound through the concrete context.
470
+
Using this approach, CGP is able remove all uses of `Async` without waiting for RTN to stabilize. Meanwhile, users who need `Send`-bound futures can rely on the proxy trait technique to recover the `Send` bound through the concrete context.
469
471
470
472
This hack is expected to be temporary. Once RTN is stabilized, the proxy traits can be eliminated entirely. Migrating to the proxy trait approach now is simpler than retaining `Async` long-term, and it avoids a potentially painful migration in the future if CGP becomes widely adopted.
471
473
472
474
### Migration Advice
473
475
474
476
For existing codebases that still rely on `Async` and the `Send`-safe variants such as `HasAsyncErrorType`, you can copy these definitions locally to continue using them in your projects.
475
477
476
-
Although CGP has removed the `Async` constructs, this does not prevent developers from enforcing `Send` bounds in their own code. The main impact is that all async traits officially defined by the `cgp` crate no longer impose a `Send` bound on the returned `Future`. If your project defines traits that require returned futures to implement `Send`, you may encounter issues when calling CGP’s async traits.
478
+
Although CGP has removed the `Async` constructs, this does not prevent developers from enforcing `Send` bounds in their own code. The main impact is that all async traits officially defined by the `cgp` crate no longer impose a `Send` bound on the returned `Future`. If your project defines traits that require returned futures to implement `Send`, you may encounter issues when calling CGP’s async traits. In other words, the split between `Send` and non-`Send` only becomes an issue if your project aims to interop with the remaining CGP ecosystem.
477
479
478
-
In the short term, the easiest way to address this is to remove all `Send` bounds in your own codebase. If that is not practical, the simplest workaround is to define your own versions of the relevant CGP traits that explicitly include `Send` in the returned `Future`.
480
+
In the short term, the easiest way to address this is to remove all `Send` bounds of async functions in your own codebase. If that is not practical, the simplest workaround is to define your own versions of the relevant CGP traits that explicitly include `Send` in the returned `Future`.
479
481
480
482
Currently, there are only a small number of async traits in CGP, such as `CanRun`, `CanComputeAsync`, and `CanHandle`. Unless your project heavily relies on these abstractions, redefining these traits locally to include `Send` should be straightforward and require minimal effort.
0 commit comments