re-frame/docs/WIP/README.md

367 lines
16 KiB
Markdown
Raw Normal View History

2016-10-20 20:07:38 +00:00
[logo](/images/logo/re-frame_128w.png?raw=true)
## Derived Values, Flowing
> This, milord, is my family's axe. We have owned it for almost nine hundred years, see. Of course,
sometimes it needed a new blade. And sometimes it has required a new handle, new designs on the
metalwork, a little refreshing of the ornamentation ... but is this not the nine hundred-year-old
axe of my family? And because it has changed gently over time, it is still a pretty good axe,
y'know. Pretty good.
> -- Terry Pratchett, The Fifth Elephant <br>
> &nbsp;&nbsp;&nbsp; Reflecting on identity, flow and derived values
[![Clojars Project](https://img.shields.io/clojars/v/re-frame.svg)](https://clojars.org/re-frame)
[![GitHub license](https://img.shields.io/github/license/Day8/re-frame.svg)](license.txt)
[![Circle CI](https://circleci.com/gh/Day8/re-frame/tree/develop.svg?style=shield&circle-token=:circle-ci-badge-token)](https://circleci.com/gh/Day8/re-frame/tree/develop)
[![Circle CI](https://circleci.com/gh/Day8/re-frame/tree/master.svg?style=shield&circle-token=:circle-ci-badge-token)](https://circleci.com/gh/Day8/re-frame/tree/master)
## Why Should You Care?
Perhaps:
1. You want to develop an [SPA] in ClojureScript, and you are looking for a framework
2016-10-22 03:56:04 +00:00
2. You believe Facebook did something magnificent when it created React, and
2016-10-20 20:07:38 +00:00
you are curious about the further implications. Is the combination of
`reactive programming`, `functional programming` and `immutable data` going to
**completely change everything**? And, if so, what would that look like in a language
that embraces those paradigms?
3. You're taking a [Functional Design and Programming course at San Diego State University](http://www.eli.sdsu.edu/courses/fall15/cs696/index.html)
2016-11-30 10:28:01 +00:00
and you have a re-frame/reagent assignment due. You've left the reading a bit late, right? I remember those days.
4. re-frame is impressively buzzword compliant: it has reactivity,
2016-10-26 20:05:05 +00:00
unidirectional data flow, pristinely pure functions,
2016-10-20 20:07:38 +00:00
interceptors, coeffects, conveyor belts, statechart-friendliness (FSM)
2016-10-26 20:05:05 +00:00
and claims an immaculate hammock conception. It also has a charming
xkcd reference (soon) and a hilarious, insiders-joke T-shirt,
2016-10-20 20:07:38 +00:00
ideal for conferences (in design). What could possibly go wrong?
## re-frame
re-frame is a pattern for writing [SPAs] in ClojureScript, using [Reagent].
This repo contains both a **description of this pattern** and
a **reference implementation**.
McCoy might report "It's MVC, Jim, but not as we know it". And you would respond
2016-11-30 10:28:01 +00:00
"McCoy, you damn trouble maker, why even mention an OO pattern?
2016-10-20 20:07:38 +00:00
re-frame is a **functional framework**."
2016-11-30 10:28:01 +00:00
Being a functional framework, it is about data, and the pure functions
which transform that data.
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
### It is a loop
2016-10-20 20:07:38 +00:00
Architecturally, re-frame implements "a perpetual loop".
To build an app, you hang pure functions on certain parts of this loop,
and re-frame looks after the `conveyance of data` (flow of data)
around the loop, into and out of the transforming functions you
provide - hence the tag line "Derived Data, Flowing".
2016-12-02 12:46:06 +00:00
## It does Physics
Remember this diagram from school? The water cycle, right?
Two distinct stages, involving water in different phases, being acted upon
by different forces: gravity working one way, evaporation/convection the other.
<img align="right" src="/images/the-water-cycle.png?raw=true">
To understand re-frame, **imagine data flowing around that loop instead of water**. re-frame
provides the "conveyance" of the data - the equivalent of gravity, evaporation and convection.
You design what's flowing and then you hang functions off the loop at
various points to look after the data's phase changes.
Sure, right now, you're thinking "lazy sod - make a proper Computer Science-y diagram". But, no.
Joe Armstrong says "don't break the laws of physics" - I'm sure
you've seen the videos - and if he says to do something, you do it
(unless Rich Hickey disagrees, and says to do something else). So,
this diagram, apart from being a plausible analogy which might encourage
you to look differently at re-frame, is **practically proof** it does physics.
2016-11-30 10:28:01 +00:00
### It is a 6-domino cascade
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
Computationally, each iteration of the loop involves a
6 domino cascade. One domino triggering the next, which triggers the next, etc,
2016-10-20 20:07:38 +00:00
until we are back at the beginning of the loop. Each iteration has the same cascade.
2016-10-20 20:25:54 +00:00
<img align="right" src="/images/Readme/Dominoes-small.jpg?raw=true">
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
#### 1st Domino
An `event` is sent when something happens - the user
clicks a button, or a websocket receives a new message.
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
Without the impulse of a triggering `event`, no 6 domino cascade occurs.
It is only because of `events` that a re-frame app is propelled,
loop iteration after loop iteration, from one state to the next.
2016-10-20 20:07:38 +00:00
re-frame is `event` driven.
2016-11-30 10:28:01 +00:00
#### 2nd Domino
In response to an `event`, an application must compute
its ramification (its ambition, its intent) - what should
happen as a result. This is known as `event handling`.
Event handler functions compute `effects`. Or, more accurately, they compute
a **description of `effects`**, which is to say they say, declaratively,
how the world should change (because of the event).
Much of the time, only the state of the SPA itself need
change, but sometimes the outside world too must be effected
(localstore, cookies, databases, emails, logs, etc).
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
#### 3rd Domino
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
These descriptions of `effects` are actioned. The intent is made real.
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
Now, to a functional programmer, `effects` are scary in a
[xenomorph kind of way](https://www.google.com.au/search?q=xenomorph).
2016-10-20 20:07:38 +00:00
Nothing messes with functional purity
quite like the need for effects and coeffects. But, on the other hand, `effects` are equally
marvelous because they take the app forward. Without them, an app stays stuck in one state forever,
never achieving anything.
So re-frame embraces the protagonist nature of `effects` - the entire, unruly zoo of them - but
it does so in a controlled, debuggable, auditable, mockable, plugable way.
2016-12-02 12:46:06 +00:00
#### Then what happens?
2016-10-20 20:07:38 +00:00
2016-12-02 12:46:06 +00:00
So, that 3rd domino just changed the world and, very often, that involves
2016-11-30 10:28:01 +00:00
**changing the app's state**.
2016-12-02 12:46:06 +00:00
re-frame `app state` is held in one place - think of it like you
2016-11-30 10:28:01 +00:00
would an in-memory, central database for the app.
2016-12-02 12:46:06 +00:00
When domino 3 changes `app state`, it triggers the next part of the cascade
involving dominoes 4-5-6.
2016-11-30 10:28:01 +00:00
#### The view formula
The 4-5-6 domino cascade implements the formula made famous by Facebook's ground-breaking React library:
`v = f(s)`. A view `v` is a function `f` of the app state `s`.
2016-12-02 12:46:06 +00:00
Or, said another way, there are functions `f` which compute what DOM nodes, `v`,
should be displayed to the user when the application is in a given app state, `s`.
2016-11-30 10:28:01 +00:00
**Over time**, when `s` changes, `f`
2016-10-20 20:07:38 +00:00
will be called again to compute new `v`, forever keeping `v` up to date with the current `s`.
2016-12-02 12:46:06 +00:00
Now, in our case, it is domino 3 which change `s`, the application state,
2016-11-30 10:28:01 +00:00
and, in response, dominoes 4-5-6 are about re-running `f` to compute the new `v`
shown to the user.
2016-10-21 05:08:16 +00:00
2016-12-02 12:46:06 +00:00
Except, of course, there's nuance. For instance, there's no single `f` to run.
2016-11-30 10:28:01 +00:00
There may be many functions which collectively build the overall DOM,
2016-10-21 05:08:16 +00:00
and only part of `s` may change at any one time, so only part of the
2016-11-30 10:28:01 +00:00
`v` (DOM) need be re-computed and updated. And some parts of `v` might not
even be showing right now.
#### Domino 4
**Domino 4** is a novel and efficient de-duplicated signal graph which
runs query functions on the app state, `s`, efficiently computing
reactive, multi-layered, "materialised views" of `s`.
(Relax about any unfamiliar terminology, you'll soon
see how very simple the code actually is)
#### Domino 5
**Domino 5** is one or more **view functions** (Reagent) which compute what
UI DOM should be displayed for the user.
They take data, delivered reactively by the queries of domino 4,
and compute hiccup-formatted data, which is a description of the DOM required.
More on hiccup soon.
#### Domino 6
**Domino 6** is not something you need write yourself - instead it is handled for you
by Reagent/Rect. I mention it here
2016-12-02 12:46:06 +00:00
for completeness in order to fully close the loop.
2016-11-30 10:28:01 +00:00
It is the step in which the hiccup-formatted
2016-12-02 12:46:06 +00:00
"descriptions of required DOM", returned by Domino 5, are made real. The
2016-11-30 10:28:01 +00:00
browser DOM nodes are mutated by React.
2016-10-21 05:08:16 +00:00
2016-12-02 12:46:06 +00:00
#### Mastering mutation
2016-10-21 05:08:16 +00:00
2016-12-02 12:46:06 +00:00
The two sub-cascades 1-2-3 and 4-5-6 have a similar structure.
2016-11-30 10:28:01 +00:00
2016-12-02 12:46:06 +00:00
The last domino in each is mutative - it does the dirty work. The step immediately
prior computes "descriptions" of the mutations required, and then these final mutative dominoes
make those descriptions real.
2016-11-30 10:28:01 +00:00
2016-12-02 12:46:06 +00:00
You seldom need worry yourself about 3 and 6. re-frame looks after that. Instead
your programming work will be done in the earlier pure-function dominoes.
2016-10-20 20:07:38 +00:00
### A Dominoes Walk Through
2016-12-02 12:46:06 +00:00
The explanation above was from 60,000 feet. Let's now move down slightly, to 30,000 feet.
Still high level, but a wiff more soil.
2016-11-30 10:28:01 +00:00
2016-12-02 12:46:06 +00:00
Imagine the UI of an SPA is showing a list of items, and that the user
clicks the "delete" button for the 3rd item in a list.
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
In response,
what happens within this re-frame app? Here's a sketch of the 6 domino cascade:
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
1. The `on-click` handler for that delete button uses the re-frame supplied function,
`dispatch`, to emit an `event`. That event might be `[:delete-item 2]`.
Yes, that's a vector of two elements. The first element says what kind of event it is,
2016-12-02 12:46:06 +00:00
and the `rest` are further details of the event.
2. The `event handler` (function), associated with `:delete-item` (the first
element of the event), is called to compute what `effect` the `event`
2016-11-30 10:28:01 +00:00
should have.
2016-10-22 03:56:04 +00:00
In this case, it computes that new application state should
2016-12-02 12:46:06 +00:00
result and that this new state will not include the to-be-deleted item.
2016-11-30 10:28:01 +00:00
3. an `effect handler` (function) actions the `effect`, and
resets application state to the newly computed value. This is a mutative
step, facilitated by re-frame, which you won't have to do explicitly.
4. because the application state changed, a query (function) over the application
state is called (reactively), and it computes the list of items (which
2016-12-02 12:46:06 +00:00
now, because of domino 3, nolonger contains the 3rd item). Because the items
are already stored in app state, there's not a lot to compute in this case. More
of an accessor.
2016-11-30 10:28:01 +00:00
5. because the query function computed a new value, a view (function) which subscribes
to that value, is called (reactively) to re-compute DOM. It produces a hiccup-formatted
2016-12-02 12:46:06 +00:00
data structure describing the DOM nodes required (no DOM nodes for the deleted item, obviously,
2016-11-30 10:28:01 +00:00
but otherwise the same DOM as last time).
6. Reagent/React takes the description of required DOM, and makes it real. The DOM "this
time" is pretty much the same as last time, except fo the absence of the DOM for that
deleted item. This is
2016-12-02 12:46:06 +00:00
a mutative step, which you don't have to worry about explicitly. It just happens.
2016-10-22 03:56:04 +00:00
At this point, the re-frame app returns to a quiescent state,
2016-11-30 10:28:01 +00:00
waiting for the next event. When a new one happens, another
2016-12-02 12:46:06 +00:00
6 domino cascade will follow.
2016-11-30 10:28:01 +00:00
2016-10-20 20:07:38 +00:00
### A Simple Loop Of Simple Functions
2016-10-22 03:56:04 +00:00
**Each of the dominoes you supply are simple, pure functions** which
can be be described, understood and
2016-11-30 10:28:01 +00:00
tested independently. They take data, transform it and return new data.
2016-10-20 20:07:38 +00:00
The loop itself is utterly predictable and very mechanical in operation.
So, there's a regularity, simplicity and
certainty to how a re-frame app goes about its business,
which leads, in turn, to a great ease in reasoning and debugging.
## It Leverages Data
You might already know that ClojureScript is a modern lisp, and that
lisps are **homoiconic**. If not, you do now.
The homoiconic bit is significant. It means you program in a lisp by creating and
2016-11-30 10:28:01 +00:00
assembling lisp data structures. Think about that. You are **programming in data**.
2016-10-20 20:07:38 +00:00
The functions which later manipulate data, start as data.
Clojure programmers place particular
emphasis on the primacy of data. When they aren't re-watching Rich Hickey videos,
and wishing their hair was darker and more curly,
2016-10-22 03:56:04 +00:00
they meditate on aphorisms like "Data is the ultimate in late binding".
2016-10-20 20:07:38 +00:00
2016-10-22 03:56:04 +00:00
I cannot stress too much what a big deal this is. It can seem
like a syntax curiosity at first but, when the penny drops for
2016-10-20 20:07:38 +00:00
you on this, it tends to be a profound moment. And once you
understand the importance of this concept at the language level,
2016-10-22 03:56:04 +00:00
you naturally want to leverage similar power at the library level.
2016-10-20 20:07:38 +00:00
So, it will come as no surprise, then, to know that re-frame has a
data oriented design. Events are data. Effects are data. DOM is data.
The functions which transform data are registered and looked up via
data. Interceptors (data) are preferred over middleware (higher
order functions). Etc.
Data - that's the way we roll.
2016-11-30 10:28:01 +00:00
### It is mature and proven in the large
2016-10-20 20:07:38 +00:00
2016-11-30 10:28:01 +00:00
re-frame was released in early 2015, and has since [been](https://www.fullcontact.com)
2016-10-26 20:05:05 +00:00
successfully
2016-10-25 06:22:49 +00:00
[used](https://www.nubank.com.br)
2016-10-26 20:05:05 +00:00
by
2016-10-25 06:22:49 +00:00
[quite](http://open.mediaexpress.reuters.com/)
2016-10-26 20:05:05 +00:00
a
[few](https://rokt.com/) companies and
2016-11-30 10:28:01 +00:00
individuals to build complex apps, many running beyond 40K lines of
ClojureScript.
2016-10-20 20:07:38 +00:00
2016-10-25 06:27:15 +00:00
<img align="right" src="/images/scale-changes-everything.jpg?raw=true">
2016-10-25 06:22:49 +00:00
2016-10-26 20:05:05 +00:00
**Scale changes everything.** Frameworks
2016-10-20 20:07:38 +00:00
are just pesky overhead at small scale - measure them instead by how they help
2016-10-21 05:08:16 +00:00
you tame the complexity of bigger apps, and in this regard re-frame has
2016-11-30 10:28:01 +00:00
worked out well. Some have been effusive in their praise.
2016-10-20 20:07:38 +00:00
Having said that, re-frame remains a work in progress and it falls
2016-10-21 05:08:16 +00:00
short in a couple of ways - for example it doesn't work as well as we'd
2016-11-30 10:28:01 +00:00
like with devcards (which is a library vs framework issue) - we're still
2016-10-21 05:08:16 +00:00
puzzling over some aspects and tweaking as we go. All libraries
represent a point in the possible design space, with pros and cons.
2016-10-20 20:07:38 +00:00
2016-10-21 05:08:16 +00:00
And, yes, re-frame is fast, straight out of the box. And, yes, it has
2016-11-30 10:28:01 +00:00
a good testing story (unit and behavioural). And, yes, it works in with figwheel to create
2016-10-21 05:08:16 +00:00
a delightful hot-loading development story. And, yes, it has
a fun specialist tooling, and a community,
2016-11-30 10:28:01 +00:00
and useful 3rd party libraries.
2016-10-20 20:07:38 +00:00
### Where Do I Go Next?
2016-11-30 10:28:01 +00:00
We haven't yet looked at code, but **at this point you
already know 40% of re-frame.** There's detail to fill in, for sure,
2016-10-22 03:56:04 +00:00
but the core concepts are now known to you.
2016-10-21 05:08:16 +00:00
2016-11-30 10:28:01 +00:00
Next, you need to do the code walk-through in the docs. This
will quickly get your knowledge to about 70%. The
final 30% always comes incrementally with use and by reading the rest of the
docs (of which there's a few).
2016-10-21 05:08:16 +00:00
2016-10-25 06:22:49 +00:00
So, next, go here: <br>
2016-10-20 20:07:38 +00:00
https://github.com/Day8/re-frame/blob/master/docs/README.md
2016-10-22 03:56:04 +00:00
Experiment with these examples: <br>
2016-10-20 20:07:38 +00:00
https://github.com/Day8/re-frame/tree/master/examples
2016-10-21 05:08:16 +00:00
Use a template to create your own project: <br>
2016-10-22 03:56:04 +00:00
Client only: https://github.com/Day8/re-frame-template <br>
Front and back: http://www.luminusweb.net/
2016-10-20 20:07:38 +00:00
2016-10-21 05:08:16 +00:00
Use these resources: <br>
2016-10-20 20:07:38 +00:00
https://github.com/Day8/re-com
XXX
2016-11-30 10:28:01 +00:00
### What Of This Romance?
My job is to be a relentless cheerleader for re-frame, right?
2016-12-02 12:46:06 +00:00
The gyrations of my Pom-Poms should be tectonic,
2016-11-30 10:28:01 +00:00
but the following quote just makes me smile. It should
be taught in all CompSci courses.
> We begin in admiration and end by organizing our disappointment <br>
> &nbsp;&nbsp;&nbsp; -- Gaston Bachelard (French philosopher)
2016-12-02 12:46:06 +00:00
Of course, that only applies if you get passionate about your technologies.
2016-11-30 10:28:01 +00:00
2016-12-02 12:46:06 +00:00
But, no. No! Those French Philosophers and their pessimism - ignore him!!
2016-11-30 10:28:01 +00:00
Your love for re-frame will be deep, abiding and enriching.
2016-10-20 20:07:38 +00:00
### Licence
Copyright © 2015 Michael Thompson
Distributed under The MIT License (MIT) - See LICENSE.txt
[SPAs]:http://en.wikipedia.org/wiki/Single-page_application
[SPA]:http://en.wikipedia.org/wiki/Single-page_application
[Reagent]:http://reagent-project.github.io/