Skip to content

Commit

Permalink
Add consumer and provider chapters (#4)
Browse files Browse the repository at this point in the history
* Add custom syntax highlighting theme

* Write consumer chapter

* Add provider chapter

* Update GitHub links
  • Loading branch information
soareschen authored Apr 11, 2024
1 parent b58d8c2 commit 1467f95
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 9 deletions.
4 changes: 0 additions & 4 deletions book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,3 @@ edition = "2021"

[build]
create-missing = false


[output.html]
default-theme = "rust"
62 changes: 61 additions & 1 deletion content/consumer.md
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.
6 changes: 3 additions & 3 deletions content/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ such as the similarity and differences of context-generic programming as compare

## Contribution

This book is open sourced under the MIT license on [GitHub](https://github.com/contextgeneric/context-generic-programming-patterns).
This book is open sourced under the MIT license on [GitHub](https://github.com/contextgeneric/cgp-patterns).

Anyone is welcome to contribute by submitting [pull requests](https://github.com/contextgeneric/context-generic-programming-patterns/pulls)
Anyone is welcome to contribute by submitting [pull requests](https://github.com/contextgeneric/cgp-patterns/pulls)
for grammatical correction, content improvement, or adding new design patterns.

A [GitHub Discussions](https://github.com/contextgeneric/context-generic-programming-patterns/discussions) forum is available for readers
A [GitHub Discussions](https://github.com/contextgeneric/cgp-patterns/discussions) forum is available for readers
to ask questions or have discussions for topics covered in this book.
79 changes: 78 additions & 1 deletion content/provider.md
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.
125 changes: 125 additions & 0 deletions theme/highlight.css
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 */
}

0 comments on commit 1467f95

Please sign in to comment.