@@ -19,8 +19,8 @@ component interface, in addition to _consumer traits_ which are used for
1919consuming a component interface.
2020
2121The separation of provider traits from consumer traits allows multiple context-generic
22- provider implementations to be defined, bypassing Rust's trait system original restriction
23- of forbidding overlapping implementations.
22+ provider implementations to be defined, bypassing Rust's trait system's original restriction
23+ that forbids overlapping implementations.
2424
2525## Expressive Ways to Write Code
2626
@@ -59,6 +59,175 @@ Since all CGP features work only at compile-time, it provides the same
5959_ zero-cost abstraction_ advantage as Rust. Applications do not have to sacrifice
6060any runtime overhead for using CGP in the code base.
6161
62+ # Hello World Example
63+
64+ We will demonstrate various concepts of CGP with a simple hello world example.
65+
66+ ## Greeter Component
67+
68+ First, we would import ` cgp ` and define a greeter component as follows:
69+
70+ ``` rust
71+ use cgp :: prelude :: * ;
72+
73+ #[derive_component(GreeterComponent , Greeter <Context >)]
74+ pub trait CanGreet {
75+ fn greet (& self );
76+ }
77+ ```
78+
79+ The ` cgp ` crate provides all common constructs inside the ` prelude ` module,
80+ which should be imported in many cases. The first CGP construct we use is
81+ the ` derive_component ` macro, which generates additional constructs for
82+ the greeter component. The macro target, ` CanGreet ` , is a _ consumer trait_
83+ that is used similar to regular Rust traits, but is not for implementation.
84+
85+ The first macro argument, ` GreeterComponent ` , is used as the _ name_ of
86+ the greeter component we defined. The second argument is used
87+ to define a _ provider trait_ called ` Greeter ` , which is used for implementing
88+ the greet component. ` Greeter ` has similar structure as the ` CanGreet ` ,
89+ but with the implicit ` Self ` type replaced with the generic type ` Context ` .
90+
91+ ## Hello Greeter
92+
93+ With the greeter component defined, we would implement a hello greeter provider
94+ as follows:
95+
96+ ``` rust
97+ pub struct GreetHello ;
98+
99+ impl <Context > Greeter <Context > for GreetHello
100+ where
101+ Context : HasField <symbol ! ("name "), Field : Display >,
102+ {
103+ fn greet (context : & Context ) {
104+ println! (
105+ " Hello, {}!" ,
106+ context . get_field (PhantomData :: <symbol! (" name" )>)
107+ );
108+ }
109+ }
110+ ```
111+
112+ The provider ` GreetHello ` is implemented as a struct, and implements
113+ the provider trait ` Greeter ` . It is implemented as a
114+ _ context-generic provider_ that can work with any ` Context ` type,
115+ but with additional constraints (or dependencies) imposed on the
116+ context.
117+
118+ In this example case, the constraint
119+ ` HasField<symbol!("name"), Field: Display> ` means that ` GreetHello `
120+ expects ` Context ` to be a struct with a field named ` name ` , with
121+ the field type being any type that implements ` Display ` .
122+
123+ The trait ` HasField ` is a CGP getter trait for accessing fields in a
124+ struct. The ` symbol! ` macro is used to convert any string literal
125+ into types, so that they can be used as type argument. The
126+ associated type ` Field ` is implemented as the type of the field in
127+ the struct.
128+
129+ The ` HasField ` trait provides a ` get_field ` method,
130+ which can be used to access a field value. The type
131+ ` PhantomData::<symbol!("name")> ` is passed to ` get_field ` to infer
132+ which field we want to read, in case if there are more than one
133+ field in scope.
134+
135+ Notice that with the separation of provider trait from consumer trait,
136+ multiple providers like ` GreetHello ` can _ all_ have generic implementation
137+ over any ` Context ` , without causing any issue of overlapping implementation
138+ imposed by Rust's trait system.
139+
140+ Additionally, the provider ` GreetHello ` can require additional
141+ constraints from ` Context ` , without those constraints bein present
142+ in the trait bound of ` CanGreet ` . This concept is sometimes known as
143+ _ dependency injection_ , as extra dependencies are "injected" into
144+ the provider through the context.
145+
146+ Compared to other languages, CGP can not only inject methods into
147+ a provider, but also _ types_ , as we seen with the ` Field ` associated
148+ type in ` HasField ` .
149+
150+ ## Person Context
151+
152+ Next, we will define a concrete context ` Person ` , and wire it up to
153+ use ` GreetHello ` to implement ` CanGreet ` :
154+
155+ ``` rust
156+ #[derive(HasField )]
157+ pub struct Person {
158+ pub name : String ,
159+ }
160+
161+ pub struct PersonComponents ;
162+
163+ impl HasComponents for Person {
164+ type Components = PersonComponents ;
165+ }
166+
167+ delegate_components! {
168+ PersonComponents {
169+ GreeterComponent : GreetHello ,
170+ }
171+ }
172+ ```
173+
174+ The ` Person ` context is defined to be a struct containing a ` name ` field,
175+ which is of type ` String ` . The CGP macro ` derive(HasField) ` is used to
176+ automatically implement ` Person: HasField<symbol!("name"), Field = String> ` ,
177+ so that it can be used by ` GreetHello ` .
178+
179+ Additionally, we also define an empty struct ` PersonComponents ` , which
180+ is used to wire up all the providers for ` Person ` . We implement the
181+ CGP trait ` HasComponents ` for ` Person ` , which sets ` PersonComponents `
182+ as its _ aggregated provider_ .
183+
184+ We use the CGP macro ` delegate_components ` to wire up the delegation of
185+ providers for ` PersonComponent ` . The macro allows multiple components
186+ to be listed in the body, in the form of ` ComponentName: Provider ` .
187+ In this example, we only have one entry, which is to use ` GreetHello `
188+ as the provider for ` GreeterComponent ` . Notice that this is where we
189+ use the component name ` GreeterComponent ` , which was defined earlier
190+ by ` derive_component ` .
191+
192+ With the expressive mapping of components to provider inside
193+ ` delegate_components! ` , we can easily switch the implementation of
194+ ` Greeter ` to another provider by making just one line of code change.
195+
196+ Note that CGP allows component wiring to be done _ lazily_ . This means
197+ that any error such as unsatisfied dependencies will only be resolved
198+ when we try to _ use_ the provider.
199+
200+ ## Calling Greet
201+
202+ Now that the wiring has been done, we can try to construct a ` Person `
203+ and then call ` greet ` on it:
204+
205+ ``` rust
206+ let person = Person {
207+ name : " Alice" . into (),
208+ };
209+
210+ // prints "Hello, Alice!"
211+ person . greet ();
212+ ```
213+
214+ If we try to build and run the above code, we will see that the code
215+ compiles successfully, and the line "Hello, Alice!" is greeted on the
216+ terminal.
217+
218+ The method ` greet ` is called from the consumer trait ` CanGreet ` , which
219+ is implemented by ` Person ` via ` PersonComponents ` , which implements
220+ ` Greeter ` via delegation of ` GreeterComponent ` to ` GreetHello ` ,
221+ which implements ` Greeter ` given that ` Person ` implements
222+ ` HasField<symbol!("name"), Field: Display> ` .
223+
224+ Hopefully by the end of this tutorial, you have gotten a sense of how
225+ it is like to program in CGP.
226+ There are a lot more to cover on how such wiring is done behind the scene
227+ by CGP, and what else we can do with CGP.
228+ You can continue and find out more by reading the book
229+ [ Context-Generic Programming Patterns] ( https://patterns.contextgeneric.dev/ ) .
230+
62231# Current Status
63232
64233As of end of 2024, CGP is still in _ early-stage_ development, with many
0 commit comments