Readme WIP

This commit is contained in:
mike-thompson-day8 2015-02-21 16:40:24 +11:00
parent e383f3c1af
commit 57b1fbc160

150
README.md
View File

@ -22,15 +22,16 @@ Todo:
alt="Leiningen logo" title="The man himself" align="right" />
-->
## Why Should You Care?
## Why Should You Care About re-frame?
Either:
1. You need to develop an app in ClojureScript, and you are looking for a framework.
2. You don't program in ClojureScript, but you have heard something about how "reactive programming"
combined with "functional programming"
principals and "immutable data" **is going to change everything**,
and you are curious to get some insight into the claim.
1. You want to develop an app in ClojureScript, and you are looking for a framework; or
2. You believe that ReactJS decisively won the javascript framework wars in early 2015 and
you are wondering how the combination of
"reactive programming", "functional programming" and "immutable data" could
**change everything**. Could this be a case of:
"The future is already here - it's just not very evenly distributed".
## re-frame
@ -47,42 +48,45 @@ To build a re-frame app, you:
Features:
1. Nearly all the functions you write are pure, so the computational pieces of your app can
be described, understood and tested independently.
2. Composition of these computational parts happens via reactive data flows - a dynamic,
unidirectional Signal graph.
3. The resulting architecture involves "derived data" flowing in a two-stage, reactive loop (cycle),
through pure functions. To put it another way, time is explicitly modeled.
4. The jarring thing about re-frame is how simple it all is. Beautifully simple! Our reference
implementation is little more than 100 lines of code.
5. But it scales up so nicely to more complex apps. Frameworks are just pesky overhead at small
scale - its how they help you to tame complexity that defines them.
1. The functions you write are pure, so the computational pieces of your app can
be described, understood and tested independently.
You just won't need a sophisticated Dependency Injection system to test. So much
incidental complexity evaporates.
2. These computational parts are composed via reactive data flows - a dynamic,
unidirectional Signal graph.
3. The resulting architecture involves "derived data" flowing in a two-stage, reactive loop.
Without realising it, you will be explicitly modeling time.
4. It is fast, straight out of the box. You won't have to go thought [this sort of pain](http://blog.scalyr.com/2013/10/angularjs-1200ms-to-35ms/).
5. The surprising thing about re-frame is how simple it is. Beautifully simple! Our reference
implementation is little more than 100 lines of (clojurescript) code. Learn it in an afternoon.
6. But it scales up so nicely to more complex apps. Frameworks are just pesky overhead at small
scale - its how they help you to tame complexity that defines them.
7. Re-frame is impressively buzzword compliant: it has FRP-nature,
unidirectional data flow, pristinely pure functions, conveyor belts, statechart-friendliness (FSM)
and claims an immaculate hammock conception.
It also has a charming xkcd reference (soon)
and a hilarious, insiders-joke T-shirt, ideal for conferences (in design).
unidirectional data flow, pristinely pure functions, conveyor belts, statechart-friendliness (FSM)
and claims an immaculate hammock conception.
It also has a charming xkcd reference (soon)
and a hilarious, insiders-joke T-shirt, ideal for conferences (in design).
**WARNING**: this document is long. That was the summary.
__Warning__: this is a long document. That was the summary.
## What Problem Does It Solve?
We first decided to build apps with ClojureScript, then we choose [Reagent], then we had a problem.
First we decided to build apps with ClojureScript, then we choose [Reagent], then we had a problem.
For all its considerable brilliance, Reagent (+ React)
only delivers the 'V' part of a traditional MVC framework.
For all its considerable brilliance, Reagent (+ ReactJS)
delivers only the 'V' part of a traditional MVC framework.
But apps involve a lot more than V. Where
But apps involve much more than V. Where
does the control logic go? How is state stored and manipulated (remembering that shared, mutable state
is the root of all evil). And how does the V bit source data?
is the root of all evil). How does the V bit source data? Etc.
We wondered: what should the rest of the architecture look like? So we read up on [Flux], [Pedestal App],
We wondered: what should the rest of the architecture look like? So we read about [Flux], [Pedestal App],
[Hoplon], [OM], [Elm], etc and, slowly but surely, re-frame emerged.
## Correct Acronym?
Is re-frame MVC? Or MVVM, or MVP? Or any of those?
Is re-frame MVC or MVP or M*P? Any of those?
Hmm, I'd say "no".
@ -90,30 +94,36 @@ Your brow furrows. "But, Mike", you protest, "I've read the document once alrea
and there's clearly a 'V' bit and there's a layer which is
'C' related, and definitely an 'M'. How can it not be MVC?"
Look, I have sympathy for your position. But if McCoy were here he'd say "It's MVC, Jim,
but not as we know it". In re-frame none of the M, V, or C bits are objects, they
Yes, that's true. But to quote McCoy: "It's MVC, Jim,
but not as we know it".
In re-frame none of the M, V, or C bits are objects, they
are pure functions (or pure data), and
and they are all wired together via reactive data flows. It is just such a different
paradigm from traditional (smalltalk) MVC. Calling it MVC would just be confusing. I'd
and they are all wired together via reactive data flows. It is just such a sufficiently different
version of (traditional, smalltalk) MVC that calling it MVC would likely just be confusing. I'd
love an alternative.
Perhaps it is a RACES framework - Reactive-Atom Component Event
Subscription framework (I love the smell of acronym in the morning).
Or, if we distill to pure essence, DDATWD - Derived Data All The Way Down.
Or, if we distill to pure essence, `DDATWD` - Derived Data All The Way Down.
*TODO:* get acronym down to 3 chars! Get an image of stacked Turtles for DDATWD
*TODO:* get acronym down to 3 chars! Get an image of stacked Turtles for `DDATWD`
insider's joke, conference T-Shirt.
## A Mashup
## Mostly A Mashup
Not much about re-frame is original or clever. You'll find
no ingenious use of functional zippers, transducers or `core.async`.
no ingenious use of functional zippers, transducers or core.async.
It does involve using Reagent's features in a slightly novel and advanced way. But
apart from that minor point, which is really a credit to Reagent, not me,
re-frame is a mashup of emerging ideas.
Re-frame does use Reagent's features in a novel way.
And we did actively reject
the current ClojureScript fashion of using Cursors (which turned
out to be a terrific decision).
But apart from that, for the most part, re-frame is a mashup of
emerging ideas.
(For the record,
one day I'd love to develop something original and clever).
@ -140,34 +150,32 @@ squint a little to see the benefits that accrue at larger scale.
## Guiding Philosophy
First, above all we believe in the one true [Dan Holmsand], creator of Reagent, and
__First__, above all we believe in the one true [Dan Holmsand], creator of Reagent, and
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.
Third, we believe that [FRP] is one honking great idea. You might be tempted to see
__Third__, we believe that [FRP] is one honking great idea. You might be tempted to see
Reagent as simply another of the React wrappers - a sibling to [OM] and
[quiescent](https://github.com/levand/quiescent).
But you'll only really "get"
Reagent when you view it as an FRP-ish library. To put that another way, we 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 like read/write `cursors` which
__Finally__, we believe in one-way data flow. No two way data binding. We don't like read/write `cursors` which
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.
re-frame does implement two way data flow (a loop of flow!), but it
uses two, separate, one-way flows to achieve it, and those two flows
are different in nature.
## FRP Clarifications
Terminology in the FRP world seems to get people hot under the collar. Those who believe in continuous-time
semantics would object to me describing re-frame as having FRP-nature. They'd claim that it does something
semantics might object to me describing re-frame as having FRP-nature. They'd claim that it does something
different from pure FRP, which is true.
But, these days, FRP seems to have become a ["big tent" (a broad church?)](http://soft.vub.ac.be/Publications/2012/vub-soft-tr-12-13.pdf).
But, these days, FRP seems to have become a
["big tent"](http://soft.vub.ac.be/Publications/2012/vub-soft-tr-12-13.pdf)
(a broad church?).
Broad enough perhaps that re-frame can be in the far, top, left paddock of the tent, via a series of
qualifications: re-frame has "discrete, dynamic, asynchronous, push FRP-ish-nature" without "glitch free" guarantees.
(Surprisingly, "glitch" has specific meaning in FRP).
@ -179,17 +187,19 @@ going further (certainly read the first two):
- [presentation (video)](http://www.infoq.com/presentations/ClojureScript-Javelin) by Alan Dipert (co-author of Hoplon)
- [serious pants Elm thesis](https://www.seas.harvard.edu/sites/default/files/files/archived/Czaplicki.pdf)
And for the love of all that is good, please watch this wonderful
And for the love of all that is good, please watch this terrific
[StrangeLoop presentation ](https://www.youtube.com/watch?v=fU9hR3kiOK0) (40 mins). Watch what happens
when you re-imagine a database as a stream!! Look at all the problems that are solved. Think about that: shared mutable state (the root of all evil),
when you re-imagine a database as a stream!! Look at all the problems that are solved.
Think about that: shared mutable state (the root of all evil),
re-imagined as a stream!! Blew my socks off.
re-frame tries to be `Derived Data everywhere, flowing`. Or perhaps,
"Derived Data All The Way Down" (an infinite loop of Derived Data). More explanation on all these claims soon.
`Derived Data All The Way Down` (an infinite loop of Derived Data).
More explanation on all these claims soon.
## Explaining re-frame
To explain re-frame, I'll incrementally develop a diagram, explaining 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
reagent tutorial and you will need to have done one of those before continuing here. Try
@ -241,8 +251,8 @@ A clarification: `app-db` doesn't actually have to be a reagent/atom containing
a map. It could, for example, be a [datascript] database. In fact, any database which is reactive
(can tell you when it changes) would do. (We'd love! to be using [datascript] - so damn cool -
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](https://gist.github.com/allgress/11348685)).
data in our apps. If you were to use it, you'd have to tweak the reference implementation a bit
[using this inspiration](https://gist.github.com/allgress/11348685)).
### Benefits Arising From Data-In-The-One-Place
@ -266,8 +276,8 @@ this is also very easy. Saving past states is trivial, and you will automaticall
good sharing guarantees to keep the size of the snapshots down.
To this list of benefits I would add two: the ability to genuinely model control via FSMs
and the ability to do time travel debugging, even in production systems. More on both soon.
To this list of benefits, 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.
@ -295,7 +305,7 @@ Richard Dawkins
### How Flow Happens In Reagent
To implement FRP-ish-ness, Reagent provides a `ratom` and a `reaction`. re-frame uses both of these
To implement FRP, Reagent provides a `ratom` and a `reaction`. re-frame uses both of these
building blocks, so let's now make sure we understand them before going further.
`ratoms` behave just like normal ClojureScript atoms. You can `swap!` and `reset!` them, `watch` them, etc.
@ -325,9 +335,9 @@ So, the `ratom` returned by a `reaction` is itself a Signal. Its value will chan
the `computation` is re-run.
So, via the interplay between `ratoms` and `reactions`, values 'flow' into computations and out
again, and then into other computations, etc. "Values" flow (propagate) through the Signal graph.
again, and then into further computations, etc. "Values" flow (propagate) through the Signal graph.
But this Signal graph will be one without cycles, because cycles cause mayhem! re-frame achieves
But this Signal graph must be without cycles, because cycles cause mayhem! re-frame achieves
a unidirectional flow.
While the mechanics are different, `reaction` has the intent of `lift` in [Elm] and `defc=` in [Hoplon].
@ -363,7 +373,8 @@ Right, so that was a lot of words. Some code to clarify:
(println @ratom3) ;; ==> "World" ;; ratom3 is automatically updated too.
```
So, in FRP-ish terms, a `reaction` will produce a "stream" of values over time (it is a Signal), accessible via the `ratom` it returns.
So, in FRP-ish terms, a `reaction` will produce a "stream" of values over time (it is a Signal),
accessible via the `ratom` it returns.
Okay, that was all important background information for what is to follow. Back to the diagram ...
@ -481,14 +492,16 @@ On with the rest of my lies and distortions...
### Components Like Templates?
A `component` like `greet` is like the templates you'd find in
A `component` such as `greet` is like the templates you'd find in
Django, Rails, Handlebars or Mustache -- it maps data to HTML -- except for two massive differences:
- you have the full power of ClojureScript available to you (generating a Clojure data structure). The
1. you have the full power of ClojureScript available to you (generating a Clojure data structure). The
downside is that these are not "designer friendly" HTML templates.
- these templates are reactive. When their input Signals change, they
2. these templates are reactive. When their input Signals change, they
are automatically rerun, producing new DOM. Reagent adroitly shields you from the details, but
the renderer of any `component` is wrapped by a `reaction`. If any of the the "inputs"
to that render change, the render is rerun.
to that render change, the render is rerun. In this sense, components are more like
[Meteor views](https://www.meteor.com/try/2).
### React etc.
@ -496,7 +509,7 @@ Okay, so we have some unidirectional, dynamic, async, discrete FRP-ish data flow
Question: To which ocean does this river of data flow? Answer: The DOM ocean.
Let's see the full picture:
The full picture:
```
app-db --> components --> Hiccup --> Reagent --> VDOM --> React --> DOM
```
@ -530,8 +543,9 @@ undergoes successive transformations, until pixels colour the monitor you to see
Derived Data, flowing. Every step is acting like a pure function and turning data into new data.
But, we don't have to bother ourselves with most of the pipeline. We just write the `components`
part and Reagent/React will look after the rest. Back to the small picture ...
All well and good, and nice to know, but we don't have to bother ourselves with most of the pipeline.
We just write the `components`
part and Reagent/React will look after the rest. So back we go to that part of the picture ...
## Subscribe