Skip to content

Commit d220e82

Browse files
committed
Reorder sections
1 parent 8aa8df7 commit d220e82

File tree

1 file changed

+163
-163
lines changed

1 file changed

+163
-163
lines changed

content/blog/2025-05-09-v0.4.0-release.md

+163-163
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,170 @@ It's been a while since the last update, but the wait is over! I'm thrilled to a
1414

1515
This post highlights the major updates developed over the past few months, alongside some personal news.
1616

17-
## Rework `#[cgp_type]` Macro
1817

18+
## Game-Changing Improvement: Debugging is Finally Easy!
19+
20+
Have you ever been frustrated by cryptic CGP errors related to unsatisfied dependencies? [Previously](/blog/early-preview-announcement/#improve-error-diagnostics), this was a major barrier to `cgp`'s wider adoption, as debugging `cgp` programs was virtually impossible due to Rust hiding the information necessary to fixed the error.
21+
22+
Crucially, this update changes everything! The most significant improvement in v0.4.0 is that it's now significantly easier to debug `cgp` errors that arise from unsatisfied dependencies. We've developed new techniques to overcome this challenge and make Rust show all errors that were previously hidden.
23+
24+
25+
### `IsProviderFor` Trait
26+
27+
In short, the technique works by introducing a new `IsProviderFor` trait in [#63](https://github.com/contextgeneric/cgp/pull/63), defined as follows:
28+
29+
```rust
30+
pub trait IsProviderFor<Component, Context, Params = ()> {}
31+
```
32+
33+
34+
The `IsProviderFor` trait itself isn't inherently complex, but it's designed to be implemented by providers with additional constraints hidden within the trait implementation. The trait then acts as a "trait-erased" trait to carry around the constraints that the provider used to implement the original provider trait.
35+
36+
Users of CGP don't need to understand the details of how `IsProviderFor` works, only that it's used behind the scenes by `cgp` to show better error messages.
37+
38+
### `CanUseComponent` Trait
39+
40+
Along with `IsProviderFor`, a new `CanUseComponent` blanket trait is introduced as a shorthand to check that a context's provider has implemented the `IsProviderFor` trait. It's defined as follows:
41+
42+
```rust
43+
pub trait CanUseComponent<Component, Params = ()> {}
44+
45+
impl<Context, Component, Params> CanUseComponent<Component, Params> for Context
46+
where
47+
Context: HasCgpProvider,
48+
Context::CgpProvider: IsProviderFor<Component, Context, Params>,
49+
{
50+
}
51+
```
52+
53+
Rather than being implemented by provider types, `CanUseComponent` is instead automatically implemented by a context type. This makes it more ergonomic to reason about the implementation of a CGP component on a context.
54+
55+
### `#[cgp_provider]` Macro
56+
57+
58+
The main change required for the new debugging to work is that users must now annotate CGP provider implementations using the `#[cgp_provider]` or `#[cgp_new_provider]` macros. For example:
59+
60+
```rust
61+
#[cgp_new_provider]
62+
impl<Context> Greeter<Context> for GreetHello
63+
where
64+
Context: HasName,
65+
{
66+
fn greet(context: &Context) {
67+
println!("Hello, {}!", context.name());
68+
}
69+
}
70+
```
71+
72+
The macro then generates the following `IsProviderFor` implementation, which includes the `Context: HasName` constraint within it:
73+
74+
```rust
75+
impl<Context> IsProviderFor<GreeterComponent, Context, ()>
76+
for GreetHello
77+
where
78+
Context: HasName
79+
{ }
80+
```
81+
82+
83+
The main difference between `#[cgp_provider]` and `#[cgp_new_provider]` is that `#[cgp_new_provider]` also generates the provider struct definition (e.g., `struct GreetHello;`), allowing even less code to be written by hand.
84+
85+
### Update to `delegate_components!`
86+
87+
88+
In addition to generating `DelegateComponent` implementations, `delegate_components!` now also generates `IsProviderFor` implementations, so that `IsProviderFor` can remain working across component delegations.
89+
90+
As an example, the following:
91+
92+
93+
```rust
94+
delegate_components! {
95+
PersonComponents {
96+
GreeterComponent: GreetHello,
97+
}
98+
}
99+
```
100+
101+
generates the following trait implementations:
102+
103+
```rust
104+
impl DelegateComponent<GreeterComponent> for PersonComponents {
105+
type Delegate = GreetHello;
106+
}
107+
108+
impl<Context, Params> IsProviderFor<GreeterComponent, Context, Params>
109+
for PersonComponents
110+
where
111+
GreetHello: IsProviderFor<GreeterComponent, Context, Params>,
112+
{
113+
}
114+
```
115+
116+
### `check_components!` Macro
117+
118+
119+
Along with the `IsProviderFor` trait, [#78](https://github.com/contextgeneric/cgp/pull/78) also introduces the `check_components!` macro to allow users to write compile-time tests to check for the correctness of component wiring for a CGP context. For example:
120+
121+
```rust
122+
check_components! {
123+
CanUsePerson for Person {
124+
GreeterComponent,
125+
}
126+
}
127+
```
128+
129+
130+
The code above generates a *check trait* called `CanUsePerson`, which verifies whether the `Person` context implements the consumer trait for `GreeterComponent` (i.e., `CanGreet`):
131+
132+
133+
```rust
134+
trait CanUsePerson<Component, Params>: CanUseComponent<Component, Params> {}
135+
136+
impl CanUsePerson<GreeterComponent, ()> for Person {}
137+
```
138+
139+
### `delegate_and_check_components!` Macro
140+
141+
PR [#84](https://github.com/contextgeneric/cgp/pull/84) introduces a new `delegate_and_check_components!` macro, which combines both `delegate_components!` and `check_components!`, allowing both delegation and checks within a single macro call. This is useful for the majority of simple cases, providing immediate feedback on whether the wiring works as intended.
142+
143+
As an example, given the following code:
144+
145+
```rust
146+
delegate_and_check_components! {
147+
CanUsePerson for Person;
148+
PersonComponents {
149+
GreeterComponent: GreetHello,
150+
}
151+
}
152+
```
153+
154+
is equivalent to writing the two separate macro calls:
155+
156+
```rust
157+
delegate_components! {
158+
PersonComponents {
159+
GreeterComponent: GreetHello,
160+
}
161+
}
162+
163+
check_components! {
164+
CanUsePerson for Person {
165+
GreeterComponent,
166+
}
167+
}
168+
```
169+
170+
171+
It's worth noting that in more advanced cases, it may still be necessary to call `delegate_components!` and `check_components` separately. This applies to cases where the CGP traits contain additional generic parameters, or when the new *preset* feature (discussed later) is used.
172+
173+
174+
### Updated Chapter
175+
176+
177+
For further details on these debugging breakthroughs, the CGP book has been updated with a [new chapter](https://patterns.contextgeneric.dev/debugging-support.html) that explains this improved debugging support in detail.
178+
179+
180+
## Rework `#[cgp_type]` Macro
19181

20182
The `cgp_type!` macro has been reworked in [#68](https://github.com/contextgeneric/cgp/pull/68) to become an attribute macro. Previously, in v0.3.0, an abstract type was defined as:
21183

@@ -197,168 +359,6 @@ pub trait HasConstant {
197359
```
198360

199361

200-
## Game-Changing Improvement: Debugging is Finally Easy!
201-
202-
Have you ever been frustrated by cryptic CGP errors related to unsatisfied dependencies? [Previously](/blog/early-preview-announcement/#improve-error-diagnostics), this was a major barrier to `cgp`'s wider adoption, as debugging `cgp` programs was virtually impossible due to Rust hiding the information necessary to fixed the error.
203-
204-
Crucially, this update changes everything! The most significant improvement in v0.4.0 is that it's now significantly easier to debug `cgp` errors that arise from unsatisfied dependencies. We've developed new techniques to overcome this challenge and make Rust show all errors that were previously hidden.
205-
206-
207-
### `IsProviderFor` Trait
208-
209-
In short, the technique works by introducing a new `IsProviderFor` trait in [#63](https://github.com/contextgeneric/cgp/pull/63), defined as follows:
210-
211-
```rust
212-
pub trait IsProviderFor<Component, Context, Params = ()> {}
213-
```
214-
215-
216-
The `IsProviderFor` trait itself isn't inherently complex, but it's designed to be implemented by providers with additional constraints hidden within the trait implementation. The trait then acts as a "trait-erased" trait to carry around the constraints that the provider used to implement the original provider trait.
217-
218-
Users of CGP don't need to understand the details of how `IsProviderFor` works, only that it's used behind the scenes by `cgp` to show better error messages.
219-
220-
### `CanUseComponent` Trait
221-
222-
Along with `IsProviderFor`, a new `CanUseComponent` blanket trait is introduced as a shorthand to check that a context's provider has implemented the `IsProviderFor` trait. It's defined as follows:
223-
224-
```rust
225-
pub trait CanUseComponent<Component, Params = ()> {}
226-
227-
impl<Context, Component, Params> CanUseComponent<Component, Params> for Context
228-
where
229-
Context: HasCgpProvider,
230-
Context::CgpProvider: IsProviderFor<Component, Context, Params>,
231-
{
232-
}
233-
```
234-
235-
Rather than being implemented by provider types, `CanUseComponent` is instead automatically implemented by a context type. This makes it more ergonomic to reason about the implementation of a CGP component on a context.
236-
237-
### `#[cgp_provider]` Macro
238-
239-
240-
The main change required for the new debugging to work is that users must now annotate CGP provider implementations using the `#[cgp_provider]` or `#[cgp_new_provider]` macros. For example:
241-
242-
```rust
243-
#[cgp_new_provider]
244-
impl<Context> Greeter<Context> for GreetHello
245-
where
246-
Context: HasName,
247-
{
248-
fn greet(context: &Context) {
249-
println!("Hello, {}!", context.name());
250-
}
251-
}
252-
```
253-
254-
The macro then generates the following `IsProviderFor` implementation, which includes the `Context: HasName` constraint within it:
255-
256-
```rust
257-
impl<Context> IsProviderFor<GreeterComponent, Context, ()>
258-
for GreetHello
259-
where
260-
Context: HasName
261-
{ }
262-
```
263-
264-
265-
The main difference between `#[cgp_provider]` and `#[cgp_new_provider]` is that `#[cgp_new_provider]` also generates the provider struct definition (e.g., `struct GreetHello;`), allowing even less code to be written by hand.
266-
267-
### Update to `delegate_components!`
268-
269-
270-
In addition to generating `DelegateComponent` implementations, `delegate_components!` now also generates `IsProviderFor` implementations, so that `IsProviderFor` can remain working across component delegations.
271-
272-
As an example, the following:
273-
274-
275-
```rust
276-
delegate_components! {
277-
PersonComponents {
278-
GreeterComponent: GreetHello,
279-
}
280-
}
281-
```
282-
283-
generates the following trait implementations:
284-
285-
```rust
286-
impl DelegateComponent<GreeterComponent> for PersonComponents {
287-
type Delegate = GreetHello;
288-
}
289-
290-
impl<Context, Params> IsProviderFor<GreeterComponent, Context, Params>
291-
for PersonComponents
292-
where
293-
GreetHello: IsProviderFor<GreeterComponent, Context, Params>,
294-
{
295-
}
296-
```
297-
298-
### `check_components!` Macro
299-
300-
301-
Along with the `IsProviderFor` trait, [#78](https://github.com/contextgeneric/cgp/pull/78) also introduces the `check_components!` macro to allow users to write compile-time tests to check for the correctness of component wiring for a CGP context. For example:
302-
303-
```rust
304-
check_components! {
305-
CanUsePerson for Person {
306-
GreeterComponent,
307-
}
308-
}
309-
```
310-
311-
312-
The code above generates a *check trait* called `CanUsePerson`, which verifies whether the `Person` context implements the consumer trait for `GreeterComponent` (i.e., `CanGreet`):
313-
314-
315-
```rust
316-
trait CanUsePerson<Component, Params>: CanUseComponent<Component, Params> {}
317-
318-
impl CanUsePerson<GreeterComponent, ()> for Person {}
319-
```
320-
321-
### `delegate_and_check_components!` Macro
322-
323-
PR [#84](https://github.com/contextgeneric/cgp/pull/84) introduces a new `delegate_and_check_components!` macro, which combines both `delegate_components!` and `check_components!`, allowing both delegation and checks within a single macro call. This is useful for the majority of simple cases, providing immediate feedback on whether the wiring works as intended.
324-
325-
As an example, given the following code:
326-
327-
```rust
328-
delegate_and_check_components! {
329-
CanUsePerson for Person;
330-
PersonComponents {
331-
GreeterComponent: GreetHello,
332-
}
333-
}
334-
```
335-
336-
is equivalent to writing the two separate macro calls:
337-
338-
```rust
339-
delegate_components! {
340-
PersonComponents {
341-
GreeterComponent: GreetHello,
342-
}
343-
}
344-
345-
check_components! {
346-
CanUsePerson for Person {
347-
GreeterComponent,
348-
}
349-
}
350-
```
351-
352-
353-
It's worth noting that in more advanced cases, it may still be necessary to call `delegate_components!` and `check_components` separately. This applies to cases where the CGP traits contain additional generic parameters, or when the new *preset* feature (discussed later) is used.
354-
355-
356-
### Updated Chapter
357-
358-
359-
For further details on these debugging breakthroughs, the CGP book has been updated with a [new chapter](https://patterns.contextgeneric.dev/debugging-support.html) that explains this improved debugging support in detail.
360-
361-
362362
## Initial Support for Datatype-Generic Programming
363363

364364
PR [#84](https://github.com/contextgeneric/cgp/pull/84) brings initial support for [datatype-generic programming](https://wiki.haskell.org/Generics) to Rust and CGP. A new `#[derive(HasFields)]` macro has been introduced, together with the relevant traits `HasFields`, `HasFieldsRef`, `FromFields`, `ToFields`, and `ToFieldsRef`.

0 commit comments

Comments
 (0)