Begin to modernize README

This commit is contained in:
Mike Thompson 2016-09-06 01:48:31 +10:00
parent 9b72c543b1
commit 6f56b72fd0

112
README.md
View File

@ -20,6 +20,10 @@ Either:
`reactive programming`, `functional programming` and `immutable data` going to `reactive programming`, `functional programming` and `immutable data` going to
**completely change everything**? And, if so, what would that look like in a language **completely change everything**? And, if so, what would that look like in a language
that embraces those paradigms? that embraces those paradigms?
3. You are curious about a `modern lisp` like Clojure. How could a language THAT
old still be so revered? And what are these whispers I hear about
"Data Oriented" architectures, and leveragable schemas. I want to expand
my mind.
## re-frame ## re-frame
@ -31,27 +35,28 @@ This repo contains both a **description of this pattern** and a **reference impl
To quote McCoy: "It's MVC, Jim, but not as we know it". To quote McCoy: "It's MVC, Jim, but not as we know it".
To build a re-frame app, you: To build a re-frame app, you:
- design your app's data structure (data layer) - design your app's information model (data layer)
- write and register subscription functions (query layer) - write and register subscription functions (query layer)
- write Reagent component functions (view layer) - write Reagent view functions (view layer)
- write and register event handler functions (control layer and/or state transition layer) - write and register event handler functions (control layer and/or state transition layer)
Features: Features:
1. The functions you write are pure, so the computational pieces of your app can 1. The functions you write are pure, so the computational pieces of your app can
be described, understood and tested independently. be described, understood and tested independently. Dependency Injection isn't needed.
You won't need Dependency Injection to test. Think about that. 2. re-frame looks after the `conveyance of data` - the transport of data between pure
2. These computational parts are composed via reactive data flows - a dynamic, functions.
3. One half of this "conveyance" involves a reactive data flow - a dynamic,
unidirectional Signal graph. unidirectional Signal graph.
3. The resulting architecture involves "derived data" flowing in a two-stage, reactive loop. 4. The resulting architecture involves "derived data" flowing in a two-stage, loop.
Without realising it, you will be explicitly modelling time. Without realising it, you will be explicitly modelling time.
4. It is fast, straight out of the box. You won't have to go through [this sort of pain](http://blog.scalyr.com/2013/10/angularjs-1200ms-to-35ms/). 5. It is fast, straight out of the box.
5. The surprising thing about re-frame is how simple it is. Beautifully simple! Our reference 6. The surprising thing about re-frame is how simple it is. The reference
implementation is little more than 200 lines of (ClojureScript) code. Learn it in an afternoon. implementation is 350 lines of (ClojureScript) code. Learn it in an afternoon.
6. But it scales up nicely to more complex apps. Frameworks are just pesky overhead at small 7. But it scales up nicely to more complex apps. Frameworks are just pesky overhead at small
scale - measure them instead by how they help you tame the complexity of bigger apps. scale - measure them instead by how they help you tame the complexity of bigger apps.
7. Re-frame is impressively buzzword compliant: it has FRP-nature, 8. Re-frame is impressively buzzword compliant: it has FRP-nature,
unidirectional data flow, pristinely pure functions, conveyor belts, statechart-friendliness (FSM) unidirectional data flow, pristinely pure functions, interceptors, conveyor belts, statechart-friendliness (FSM)
and claims an immaculate hammock conception. and claims an immaculate hammock conception.
It also has a charming xkcd reference (soon) It also has a charming xkcd reference (soon)
and a hilarious, insiders-joke T-shirt, ideal for conferences (in design). and a hilarious, insiders-joke T-shirt, ideal for conferences (in design).
@ -112,12 +117,14 @@ delivers only the 'V' part of a traditional MVC framework.
But apps involve much more than V. Where But apps involve much more than V. Where
does the control logic go? How is state stored & manipulated? etc. does the control logic go? How is state stored & manipulated? etc.
We read up on [Flux], [Pedestal App], We read up on [Pedestal App], [Flux],
[Hoplon], [Om], [Elm], etc and re-frame is the architecture that emerged. [Hoplon], [Om], early [Elm], etc and re-frame is the architecture that
emerged. Since that time we have kept a close eye on developments like
Elm Architecture, Om.Next, BEST, Cycle.js, etc. They have taught us much.
re-frame does have M, V, and C parts but they aren't objects, they re-frame does have M, V, and C parts but they aren't objects, they
are pure functions (or pure data), are pure functions (or pure data),
and they are wired together via reactive data flows. It is sufficiently different in nature and they are wired together via data flows. It is sufficiently different in nature
from (traditional, Smalltalk) MVC that calling it MVC would be confusing. I'd from (traditional, Smalltalk) MVC that calling it MVC would be confusing. I'd
love an alternative. love an alternative.
@ -138,17 +145,19 @@ his divine instrument the `ratom`. We genuflect towards Sweden once a day.
__Second__, we believe in ClojureScript, immutable data and the process of building __Second__, we believe in ClojureScript, immutable data and the process of building
a system out of pure functions. a system out of pure functions.
__Third__, we believe that [FRP] is one honking great idea. You might be tempted to see __Third__, we believe in the primacy of data. Within the Clojure community this view is
Reagent as simply another of the React wrappers - a sibling to [OM] and sometimes captured with aphorisms like "data is the ultimate in late binding" and
[quiescent](https://github.com/levand/quiescent). "data > functions > macros". There is also talk of Data Oriented Architectures.
But you'll only really "get"
Reagent when you view it as an FRP-ish library. To put that another way, I think
that Reagent, at its best, is closer in nature to [Hoplon] or [Elm] than it is OM.
__Finally__, we believe in one-way data flow. No two way data binding. We don't __Fourth__, we believe that [Reactive Programming] is one honking great idea.
like read/write `cursors` which But only up to a point. It is a beautiful solution to one half of the
data conveyance problem.
__Finally__, we believe in [command-query separation](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation).
As a result, we specifically don't like two way data binding or read/write `cursors` which
promote the two way flow of data. As programs get bigger, we've found that their promote the two way flow of data. As programs get bigger, we've found that their
use seems to encourage control logic into all the wrong places. use seems to encourage control logic into all the wrong places. Sadly, each generation of
programmers seems to have to rediscover this principle.
## FRP Clarifications ## FRP Clarifications
@ -186,8 +195,8 @@ More explanation on all these claims soon.
To explain re-frame, I'll incrementally develop a diagram, describing each part as it is added. To explain re-frame, I'll incrementally develop a diagram, describing each part as it is added.
Initially, I'll be using [Reagent] at an intermediate to advanced level. But this is no introductory I'll be using [Reagent] at an intermediate level, so you will need to have done some
reagent tutorial and you will need to have done one of those before continuing here. Try introductory Reagent tutorials before going on. Try
[The Introductory Tutorial](http://reagent-project.github.io/) or [The Introductory Tutorial](http://reagent-project.github.io/) or
[this one](https://github.com/jonase/reagent-tutorial) or [this one](https://github.com/jonase/reagent-tutorial) or
[Building Single Page Apps with Reagent](http://yogthos.net/posts/2014-07-15-Building-Single-Page-Apps-with-Reagent.html). [Building Single Page Apps with Reagent](http://yogthos.net/posts/2014-07-15-Building-Single-Page-Apps-with-Reagent.html).
@ -205,8 +214,9 @@ Our re-frame diagram starts (very modestly) with Fogus' ***well-formed data at
app-db app-db
``` ```
re-frame says that you put your data into one place which we'll call `app-db`. Structure re-frame says that you put your data into one place which we'll
the data in that place, of course, and [give it a schema](https://github.com/Prismatic/schema). call `app-db`. You should specify an information model for the
data in this one place, [giving it a powerful schema](http://clojure.org/about/spec).
Now, this advice is not the slightest bit controversial for 'real' databases, right? Now, this advice is not the slightest bit controversial for 'real' databases, right?
You'd happily put all your well-formed data into PostgreSQL or MySQL. You'd happily put all your well-formed data into PostgreSQL or MySQL.
@ -218,7 +228,7 @@ spent your life breaking systems into pieces, organised around behaviour and try
to hide the data. I still wake up in a sweat some nights thinking about all to hide the data. I still wake up in a sweat some nights thinking about all
that Clojure data lying around exposed and passive. that Clojure data lying around exposed and passive.
But, as Fogus reminds us, data at rest is the easy bit. Believe. But, as Fogus reminded us, data at rest is the easy bit.
From here on in this document, we'll assume `app-db` is one of these: From here on in this document, we'll assume `app-db` is one of these:
@ -226,6 +236,9 @@ From here on in this document, we'll assume `app-db` is one of these:
(def app-db (reagent/atom {})) ;; a Reagent atom, containing a map (def app-db (reagent/atom {})) ;; a Reagent atom, containing a map
``` ```
The reference implementation already creates and manages an internal `app-db` for you,
you don't need to declare one yourself.
Although it is a `Reagent atom` (hereafter `ratom`), I'd encourage you to think of it as an in-memory database. Although it is a `Reagent atom` (hereafter `ratom`), I'd encourage you to think of it as an in-memory database.
It will contain structured data. You will need to query that data. You will perform CRUD It will contain structured data. You will need to query that data. You will perform CRUD
and other transformations on it. You'll often want to transact on this and other transformations on it. You'll often want to transact on this
@ -237,34 +250,26 @@ a map. It could, for example, be a [datascript] database. In fact, any databas
(can tell you when it changes) would do. (We'd love! to be using [datascript] - so damn cool - (can tell you when it changes) would do. (We'd love! to be using [datascript] - so damn cool -
but we had too much but we had too much
data in our apps. If you were to use it, you'd have to tweak the reference implementation a bit, data in our apps. If you were to use it, you'd have to tweak the reference implementation a bit,
[perhaps using this inspiration](https://gist.github.com/allgress/11348685)). The reference implementation already creates and manages an internal `app-db` for you, you don't need to declare one yourself. [perhaps using this inspiration](https://gist.github.com/allgress/11348685)).
### The Benefits Of Data-In-The-One-Place ### The Benefits Of Data-In-The-One-Place
I'm going to quote verbatim from [Elm]'s website: Here's the big one: because there is a single source of truth, we do not have to write code which
synchronizes state between many different stateful components. I cannot stress too much
how significant this is. You end up writing less code
and an entire class of bugs is eliminated. The simplicity it brings is breathtaking.
1. There is a single source of truth. Traditional approaches force you to write a decent amount Further benefits also flow:
of custom and error prone code to synchronize state between many different stateful components. 1. Undo/Redo [becomes straight forward to implement](https://github.com/Day8/re-frame-undo).
(The state of this widget needs to be synced with the application state, which needs to be With a central store for state, it is easy
synced with some other widget, etc.) By placing all of your state in one location, you to snapshot and restore. This is particularly the case because the data is immutable.
eliminate an entire class of bugs in which two components get into inconsistent states. We 2. The data in `app-db` has a strong schema so, at any moment, we can validate
also think you will end up writing much less code. That has been our observation in Elm so far. all the data in the system. All of it. This enables us to catch errors easily and early. It
increases confidence in the way that Types increases confidence, only [schemas can potentially supply more
2. Save and Undo become quite easy. Many applications would benefit from the ability to save leverage than types](https://www.youtube.com/watch?v=nqY4nUMfus8).
all application state and send it off to the server so it can be reloaded at some later date. 3. The ability to genuinely model control via FSMs (discussed later)
This is extremely difficult when your application state is spread all over the place and 4. The ability to do time travel debugging, even in a production setting. More soon.
potentially tied to objects that cannot be serialized. With a central store, this becomes
very simple. Many applications would also benefit from the ability to easily undo user's
actions. For example, a painting app is better with Undo. Since everything is immutable in Elm,
this is also very easy. Saving past states is trivial, and you will automatically get pretty
good sharing guarantees to keep the size of the snapshots down.
To this list, I would briefly add two: the ability to genuinely model control via FSMs
and the ability to do time travel debugging, even in a production setting. More on both soon.
[Hoplon] takes the same approach via what they called `stem cells`, which is a root source of data.
## Flow ## Flow
@ -1099,7 +1104,6 @@ The second one is conceptually FRP, but you do have to squint.
All the parts are simple. The parts are easy to understand in isolation. The parts are composed so that All the parts are simple. The parts are easy to understand in isolation. The parts are composed so that
derived data flows in a perpetual reactive loop, through pure functions. derived data flows in a perpetual reactive loop, through pure functions.
To build an app using re-frame, you'll have to: To build an app using re-frame, you'll have to:
- design your app's data structure. - design your app's data structure.
- write and register subscription functions (query layer). - write and register subscription functions (query layer).
@ -1113,7 +1117,7 @@ Next:
- look at the examples: https://github.com/Day8/re-frame/tree/master/examples - look at the examples: https://github.com/Day8/re-frame/tree/master/examples
- read the docs: https://github.com/Day8/re-frame/blob/master/docs/README.md - read the docs: https://github.com/Day8/re-frame/blob/master/docs/README.md
After that: Then:
- use the lein template: https://github.com/Day8/re-frame-template - use the lein template: https://github.com/Day8/re-frame-template
Also, if you want reusable layout and widget components, consider this sister project: Also, if you want reusable layout and widget components, consider this sister project: