Skip to content

Commit

Permalink
Some docs. And docz :)
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Dec 14, 2018
1 parent f13cdff commit c270f7b
Show file tree
Hide file tree
Showing 11 changed files with 7,110 additions and 235 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ yarn-error.log
/react
/dist
.rpt2_cache
/coverage
/coverage
.docz
109 changes: 4 additions & 105 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,111 +3,10 @@

[![npm](https://img.shields.io/npm/v/rval.svg)](https://www.npmjs.com/package/rval) [![size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/rval/core/index.module.js?compression=gzip)](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/rval/core/index.module.js) [![install size](https://packagephobia.now.sh/badge?p=rval)](https://packagephobia.now.sh/result?p=rval) [![Build Status](https://travis-ci.org/mweststrate/rval.svg?branch=master)](https://travis-ci.org/mweststrate/rval) [![Coverage Status](https://coveralls.io/repos/github/mweststrate/rval/badge.svg?branch=master)](https://coveralls.io/github/mweststrate/rval?branch=master) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michelweststrate)

# The Philosophy of RVal

Why reactive values? In essence most of our programming work consists of transfering in-memory information from one place to another, transforming the information into new information, that is either human or machine digestable.
Data tranformations always introduces reduces redundant copies of data that need to be kept in sync with the original data.
In very trivial example of this problem might look like:

```javascript
const user = {
firstName: "Jane",
lastName: "Stanford",
fullName: "Jane Stanford"
}

document.body.innerHTML = `<h1>Hello ${user.fullName}</h1>
```

This simple snippet introduces a redundant copy of the original user's name in the `fullName` property, and in the DOM.
Now it has become the programmers responsibility to make sure any futher changes to the `user` are propagated properly:
```javascript
function updateFirstName(newName) {
user.firstName = newName
user.fullName = user.firstName + " " + user.lastName
document.body.innerHTML = `<h1>Hello ${user.fullName}</h1>
}
```
This is the problem that any state management abstraction, regardless the framework or paradigm that is used, is trying to solve.
RVal introduces a handful of primitives that help you to solve this problem in any context, by automating the question:
_when_ should _which_ transformation be applied?
Here is a quick overview in how RVal helps solving that problem.
First, we should recognize that imperatively computing new information, such as the DOM represantation, introduces stale values.
However, we can avoid ever storing such information by storing _computations_, rather than _values_.
The process for that is as simple as creating a _thunk_ (argumentless function) that capture the computation, rather than imperatively producing new values:
```javascript
const user = {
firstName: "Jane",
lastName: "Stanford",
fullName: () => user.firstName + " " + user.lastName
}
const rendering = () => `<h1>Hello ${user.fullName()}</h1>`
document.body.innerHTML = rendering()
function updateFirstName(newName) {
user.firstName = newName
document.body.innerHTML = rendering()
}
```
We've made things slightly better now; we don't have to imperatively update `user.fullName` anymore if the name changes.
Similarly, we could captured the rendered representation of the user in the thunk called `rendering`.
By storing computations instead of values, we have reduced the amount of redundant information.
However, we still have to make sure that our changes are propagated, for example by updating the DOM whenever we change the `firstName` property.
But, what if we could _subscribe_ to our thunks? And thereby avoid the need to manually propagate state changes, and increasing decoupling in the process?
In other words, what if we could write something like:
```javascript
const user = { /* as-is */ }
const rendering = () => `<h1>Hello ${user.fullName()}</h1>`
on(rendering, () => {
document.body.innerHTML = rendering()
})
function updateFirstName(newName) {
user.firstName = newName
}
```
Well, here is the good news: This is exactly the kind of things RVal allows you to write, by introducing three concepts:
1. `val(value)` to create pieces of information that can change over time
2. `drv(thunk)` to create thunks that can be subscribed to
3. `sub(something, listener)` to create a listener that fires whenever the provided reactive value or thunk changes
With those concepts, we can rewrite our above listing as a combination of reactive values and thunks, that propagate the changes when needed!
```javascript
import { val, drv, sub } from "rval"
const user = {
firstName: val("Jane"),
lastName: val("Stanford"),
fullName: drv(() => user.firstName() + " " + user.lastName())
}
const rendering = drv(`<h1>Hello ${user.fullName()}</h1>`)
// subscribe to the 'rendering' thunk
sub(rendering, () => {
document.body.innerHTML = rendering()
})
function updateFirstName(newName) {
// change the `firstName` reactive value to 'newName'
// rval will make sure that any derivation and subscription impacted by this
// change will be re-evaluated (and nothing more).
user.firstName(newName)
}
```
TODO: publish docz docs somewhere and link

[Philosophy](docs/philosophy.mdx)
[Getting Started](docs/getting-started.mdx)

# concepts

Expand Down
54 changes: 54 additions & 0 deletions docs/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Getting started with RVal

The core of `RVal` is four functions which have all a very simple contract: `val`, `drv`, `sub` and `act`.
Yes, they all have three-letter names. That's kind of cool I think. Not sure yet why.

## Creating reactive values using `val(initialValue)`

In RVal, the universe revolves around _reactive values_.
Creating your first reactive value is easy:

```javascript
import { val } from "rval";

const myLuckyNumber = val(13)
```

`val` returns a function that returns any value you've put into it.
So `myLuckyNumber` is now a number and we can call it:

```javascript
console.log(myLuckyNumber())
// prints: 13
```

Fancy! But what use is it to create a function that just returns the original value?
We'll find out in a bit.
First, there is another trick the function can do: We can call it with a new lucky number,
since the `13` didn't work out after all:

```javascript
myLuckyNumber(42)

console.log(myLuckyNumber())
// prints: 42
```

By passing an argument to the function, we can update it's internal state.
When calling the reactive value function without argumens, it will always return the value we've passed into it the last time.

You can put any value you like into a reactive value.
But, for all practical purposes, you've should consider this value to be immutable.
This will greatly benefit the understanding of the code base once it grows big enough.
But, more on that later.

See the [Philosophy](docs-philosophy) for some more background on this idea.


Drv / odd numbers

subscribe

act

Forward ref to objects
199 changes: 199 additions & 0 deletions docs/philosophy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@

# The Philosophy of RVal

Why reactive values? In essence most of our programming work consists of transfering in-memory information from one place to another, transforming the information into new information, that is either human or machine digestable.
Data tranformations always introduces reduces redundant copies of data that need to be kept in sync with the original data.
In very trivial example of this problem might look like:

```javascript
const user = {
firstName: "Jane",
lastName: "Stanford",
fullName: "Jane Stanford"
}

document.body.innerHTML = `<h1>Hello ${user.fullName}</h1>
```

This simple snippet introduces a redundant copy of the original user's name in the `fullName` property, and in the DOM.
Now it has become the programmers responsibility to make sure any futher changes to the `user` are propagated properly:
```javascript
function updateFirstName(newName) {
user.firstName = newName
user.fullName = user.firstName + " " + user.lastName
document.body.innerHTML = `<h1>Hello ${user.fullName}</h1>
}
```
This is the problem that any state management abstraction, regardless the framework or paradigm that is used, is trying to solve.
RVal introduces a handful of primitives that help you to solve this problem in any context, by automating the question:
_when_ should _which_ transformation be applied?
Here is a quick overview in how RVal helps solving that problem.
First, we should recognize that imperatively computing new information, such as the DOM represantation, introduces stale values.
However, we can avoid ever storing such information by storing _computations_, rather than _values_.
The process for that is as simple as creating a _thunk_ (argumentless function) that capture the computation, rather than imperatively producing new values:
```javascript
const user = {
firstName: "Jane",
lastName: "Stanford",
fullName: () => user.firstName + " " + user.lastName
}
const rendering = () => `<h1>Hello ${user.fullName()}</h1>`
document.body.innerHTML = rendering()
function updateFirstName(newName) {
user.firstName = newName
document.body.innerHTML = rendering()
}
```
We've made things slightly better now; we don't have to imperatively update `user.fullName` anymore if the name changes.
Similarly, we could captured the rendered representation of the user in the thunk called `rendering`.
By storing computations instead of values, we have reduced the amount of redundant information.
However, we still have to make sure that our changes are propagated, for example by updating the DOM whenever we change the `firstName` property.
But, what if we could _subscribe_ to our thunks? And thereby avoid the need to manually propagate state changes, and increasing decoupling in the process?
In other words, what if we could write something like:
```javascript
const user = { /* as-is */ }
const rendering = () => `<h1>Hello ${user.fullName()}</h1>`
on(rendering, () => {
document.body.innerHTML = rendering()
})
function updateFirstName(newName) {
user.firstName = newName
}
```
Well, here is the good news: This is exactly the kind of things RVal allows you to write, by introducing three concepts:
1. `val(value)` to create pieces of information that can change over time
2. `drv(thunk)` to create thunks that can be subscribed to
3. `sub(something, listener)` to create a listener that fires whenever the provided reactive value or thunk changes
With those concepts, we can rewrite our above listing as a combination of reactive values and thunks, that propagate the changes when needed!
```javascript
import { val, drv, sub } from "rval"
const user = {
firstName: val("Jane"),
lastName: val("Stanford"),
fullName: drv(() => user.firstName() + " " + user.lastName())
}
const rendering = drv(`<h1>Hello ${user.fullName()}</h1>`)
// subscribe to the 'rendering' thunk
sub(rendering, () => {
document.body.innerHTML = rendering()
})
function updateFirstName(newName) {
// change the `firstName` reactive value to 'newName'
// rval will make sure that any derivation and subscription impacted by this
// change will be re-evaluated (and nothing more).
user.firstName(newName)
}
```
## Functions solidifying state
At this point you might be wondering:
"But _why_ is it interesting to trap our state inside these `val` functions?"
By trapping all our pieces of state inside `val` functions,
we achieved a very interesting property:
We've practically forced ourselfs to have a single source of truth.
Instead of passing the _values of our state_ around, we can now pass a _references to our state_ around.
The benefit of this that it will stop us from accidentally creating redundant copies of our state.

Take for example the following contrived function.
It creates a random number generator, which is more likely to generate our lucky number than any other number:

```javascript
function createNumberGenerator(luckyNumber) {
return function numberGenerator() {
return Math.random() < 0.5 ? luckyNumber : Math.round(Math.random() * 100)
}
}

let luckyNumber = 13
const generator = createNumberGenerator(luckyNumber)
console.log(generator()) // 13
console.log(generator()) // 50
console.log(generator()) // 49

luckyNumber = 42
console.log(generator()) // 28
console.log(generator()) // 13
console.log(generator()) // 13
```

Now at this point, updating our `luckyNumber` variable doesn't get reflected in the `numberGenerator` anymore.
We are forced now to create a new number generator to reflect the change in our preference.
The problem is that the `luckyNumber` has been "trapped" as argument to `createNumberGenerator`.
The argument is basically a _copy_ of the original `luckyNumber` variable.

However, it is easy to see that by passing _functions_ around, we avoid this whole problem.
Because `luckyNumber` itself now becomes a `const` reference to the function that traps our lucky number.
(Yes, `let` and `var` really become anti-patterns when using `rval`).


```javascript
function createNumberGenerator(luckyNumber) {
return function numberGenerator() {
// luckyNumber get's evaluated lazily when generating numbers
return Math.random() < 0.5 ? luckyNumber() : Math.round(Math.random() * 100)
}
}

const luckyNumber = val(13) // luckyNumber is a const now!
const generator = createNumberGenerator(luckyNumber)
console.log(generator()) // 13
console.log(generator()) // 13
console.log(generator()) // 22

luckyNumber(42) // change our minds
console.log(generator()) // 42
console.log(generator()) // 8
console.log(generator()) // 42
```

By capturing values in functions, it becomes much more explicit when we want to pass a _reference_, and when a _value_.
If we want our number generator to take a one-time snapshot of the state as `luckyNumber` we can be explicit about it and _explicitly_ pass a copy of the current state `createNumberGenerator(luckyNumber())`.
On the other hand, we can also explicitly pass a reference to the state by just passing the `luckyNumber` function itself as we did above.

As it turns out, in many cases it is very intersting to pass around a reference instead of a value.
Especially when we are building systems that are supposed to be reactive, such as a user interface.
But that for later sections.

---

Note that the essence of `val` is simply this:

```javascript
function val(initial) {
let state = initial
return function() {
if (!arguments.length)
return state
state = arguments[0]
}
}
```

RVal's implementation is a little more involved, but that is because it is possible to subscribe to the `state` of a `val`.
But the above is how you can conceptually think about them.

But, let's [get started](http://localhost:3000/docs-getting-started) with the core "rval" api first!
6 changes: 6 additions & 0 deletions doczr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
menu: [
'Getting started',
'Philosophy'
],
}
Loading

0 comments on commit c270f7b

Please sign in to comment.