-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add consumer and provider chapters (#4)
* Add custom syntax highlighting theme * Write consumer chapter * Add provider chapter * Update GitHub links
- Loading branch information
1 parent
b58d8c2
commit 1467f95
Showing
5 changed files
with
267 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,3 @@ edition = "2021" | |
|
||
[build] | ||
create-missing = false | ||
|
||
|
||
[output.html] | ||
default-theme = "rust" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,62 @@ | ||
# Consumer | ||
|
||
# Consumer | ||
In CGP, a _consumer_ is a piece of code that consumes certain functionalities from a context. | ||
There are several ways which a consumer may consume a functionality. At its most basic, | ||
if a consumer has access to the _concrete_ type of a context, it can access any methods | ||
defined by an `impl` block of that context. | ||
|
||
```rust | ||
struct Person { name: String } | ||
|
||
impl Person { | ||
fn name(&self) -> &str { | ||
&self.name | ||
} | ||
} | ||
|
||
fn greet(person: &Person) { | ||
println!("Hello, {}!", person.name()); | ||
} | ||
``` | ||
|
||
in the above example, we have a `greet` function that prints a greeting to a person using | ||
the method `Person::name`. In other words, we say that the `greet` function is a _consumer_ | ||
to the `Person::name` method. | ||
|
||
## Context-Generic Consumers | ||
|
||
The `greet` function in our previous example can only work with the `Person` struct. However, | ||
if we inspect the implementation of `greet`, we can see that it is possible to generalize | ||
`greet` to work with _any_ type that has a name. | ||
|
||
To generalize `greet`, we first need to define a _trait_ that acts as an _interface_ for getting | ||
a name: | ||
|
||
```rust | ||
trait HasName { | ||
fn name(&self) -> &str; | ||
} | ||
|
||
fn greet<Context>(context: &Context) | ||
where | ||
Context: HasName | ||
{ | ||
println!("Hello, {}", context.name()); | ||
} | ||
``` | ||
|
||
In the example above, we define a `HasName` trait that provides a `name` method. We then redefine | ||
`greet` to work generically with any `Context` type, with the `where` clause requiring `Context` to | ||
implement `HasName`. Inside the function body, we call the `name` method, and print out the greeting | ||
of that name. | ||
|
||
Notice that in this example, we are able to implement `greet` _before_ we have any concrete implementation | ||
of `HasName`. Compared to before, `greet` is now _decoupled_ from the `Person` type, thus making | ||
our code more modular. | ||
|
||
In CGP, this new version of `greet` is considered a _context-generic_ consumer, as it is able to _generically_ | ||
consume the `HasName::name` method from any `Context` type that implements `HasName`. | ||
|
||
The concept of context-generic consumer is not unique to CGP. In fact, it is already commonly used | ||
in most of the Rust code that uses traits. However, we make an effort to study this concept, so that | ||
we can further generalize the concept in the later chapters of this book. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,79 @@ | ||
# Provider | ||
|
||
# Provider | ||
In CGP, a _provider_ is a piece of code that _implements_ certain functionality | ||
for a context. At its most basic, a provider is consist of an `impl` block for | ||
a trait. | ||
|
||
```rust | ||
trait HasName { | ||
fn name(&self) -> &str; | ||
} | ||
|
||
struct Person { name: String } | ||
|
||
impl HasName for Person { | ||
fn name(&self) -> &str { | ||
&self.name | ||
} | ||
} | ||
``` | ||
|
||
In the above example, we implement the `HasName` for the `Person` struct. | ||
The block `impl HasName for Person` is a _provider_ of the `HasName` trait | ||
for the `Person` context. | ||
|
||
Similar to the concept of a consumer, the use of provider is common in any | ||
Rust code that implements a trait. However, compared to cosumers, there | ||
are limitations on how providers can be defined in Rust. | ||
|
||
For this example, the `impl` block is a _context-specific_ provider for the | ||
`Person` context. Furthermore, due to the restrictions of Rust's trait system, | ||
there can be at most one provider of `HasName` for the `Person` context. | ||
Another common restriction is that the provider has to be defined in the same | ||
crate as either the trait or the context. | ||
|
||
The asymetry between what can be done with a provider, as compared to a consumer, | ||
is often a source of complexity in many Rust programs. As we will learn in later chapters, | ||
one of the goals of CGP is to break this asymetry, and make it easy to implement | ||
_context-generic providers_. | ||
|
||
## Providers as Consumers | ||
|
||
Although we have providers and consumers as distinct concepts, it is common to | ||
have code that serve as _both_ providers and consumers. | ||
|
||
```rust | ||
# trait HasName { | ||
# fn name(&self) -> &str; | ||
# } | ||
# | ||
# struct Person { name: String } | ||
# | ||
# impl HasName for Person { | ||
# fn name(&self) -> &str { | ||
# &self.name | ||
# } | ||
# } | ||
# | ||
trait CanGreet { | ||
fn greet(&self); | ||
} | ||
|
||
impl CanGreet for Person { | ||
fn greet(&self) { | ||
println!("Hello, {}!", self.name()); | ||
} | ||
} | ||
``` | ||
|
||
The example above shows a new `CanGreet` trait, which provides a `greet` method. | ||
We then implement `CanGreet` for `Person`, with the `greet` implementation using | ||
`self.name()` to print out the name to be greeted. | ||
|
||
Here, the block `impl CanGreet for Person` is a provider of `CanGreet` for the `Person` | ||
context. At the same time, it is also the _consumer_ of `HasName` for the `Person` context. | ||
In terms of genericity, the example code is _context-specific_ to the `Person` context for both | ||
the consumer and provider side. | ||
|
||
As we will see in later chapters, a powerful idea introduced by CGP is that a piece of code | ||
can have _multiple spectrums_ of genericity on the consumer and provider sides. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/*! | ||
Theme: GitHub | ||
Description: Light theme as seen on github.com | ||
Author: github.com | ||
Maintainer: @Hirse | ||
Updated: 2021-05-15 | ||
Outdated base version: https://github.com/primer/github-syntax-light | ||
Current colors taken from GitHub's CSS | ||
*/ | ||
|
||
.hljs { | ||
color: #24292e; | ||
background: #ffffff; | ||
} | ||
|
||
.hljs-doctag, | ||
.hljs-keyword, | ||
.hljs-meta .hljs-keyword, | ||
.hljs-template-tag, | ||
.hljs-template-variable, | ||
.hljs-type, | ||
.hljs-variable.language_ { | ||
/* prettylights-syntax-keyword */ | ||
color: #d73a49; | ||
} | ||
|
||
.hljs-title, | ||
.hljs-title.class_, | ||
.hljs-title.class_.inherited__, | ||
.hljs-title.function_ { | ||
/* prettylights-syntax-entity */ | ||
color: #6f42c1; | ||
} | ||
|
||
.hljs-attr, | ||
.hljs-attribute, | ||
.hljs-literal, | ||
.hljs-meta, | ||
.hljs-number, | ||
.hljs-operator, | ||
.hljs-variable, | ||
.hljs-selector-attr, | ||
.hljs-selector-class, | ||
.hljs-selector-id { | ||
/* prettylights-syntax-constant */ | ||
color: #005cc5; | ||
} | ||
|
||
.hljs-regexp, | ||
.hljs-string, | ||
.hljs-meta .hljs-string { | ||
/* prettylights-syntax-string */ | ||
color: #032f62; | ||
} | ||
|
||
.hljs-built_in, | ||
.hljs-symbol { | ||
/* prettylights-syntax-variable */ | ||
color: #e36209; | ||
} | ||
|
||
.hljs-comment, | ||
.hljs-code, | ||
.hljs-formula { | ||
/* prettylights-syntax-comment */ | ||
color: #6a737d; | ||
} | ||
|
||
.hljs-name, | ||
.hljs-quote, | ||
.hljs-selector-tag, | ||
.hljs-selector-pseudo { | ||
/* prettylights-syntax-entity-tag */ | ||
color: #22863a; | ||
} | ||
|
||
.hljs-subst { | ||
/* prettylights-syntax-storage-modifier-import */ | ||
color: #24292e; | ||
} | ||
|
||
.hljs-section { | ||
/* prettylights-syntax-markup-heading */ | ||
color: #005cc5; | ||
font-weight: bold; | ||
} | ||
|
||
.hljs-bullet { | ||
/* prettylights-syntax-markup-list */ | ||
color: #735c0f; | ||
} | ||
|
||
.hljs-emphasis { | ||
/* prettylights-syntax-markup-italic */ | ||
color: #24292e; | ||
font-style: italic; | ||
} | ||
|
||
.hljs-strong { | ||
/* prettylights-syntax-markup-bold */ | ||
color: #24292e; | ||
font-weight: bold; | ||
} | ||
|
||
.hljs-addition { | ||
/* prettylights-syntax-markup-inserted */ | ||
color: #22863a; | ||
background-color: #f0fff4; | ||
} | ||
|
||
.hljs-deletion { | ||
/* prettylights-syntax-markup-deleted */ | ||
color: #b31d28; | ||
background-color: #ffeef0; | ||
} | ||
|
||
.hljs-char.escape_, | ||
.hljs-link, | ||
.hljs-params, | ||
.hljs-property, | ||
.hljs-punctuation, | ||
.hljs-tag { | ||
/* purposely ignored */ | ||
} |