Skip to content

Commit 47dce74

Browse files
author
Steve Krouse
committed
continued editing frp paper
1 parent a335012 commit 47dce74

File tree

1 file changed

+68
-19
lines changed

1 file changed

+68
-19
lines changed

drafts/frp.md

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ If you wanted to understand the entirety of a software project, you'd have to re
1111

1212
<iframe width="560" height="500" src="https://www.desmos.com/calculator/gzeqaru5pe?embed" frameborder="0" allowfullscreen></iframe>
1313

14-
Most programming languages are not modularly comprehensible in such a linear fashion but in an exponential one, as demonstrated in the yellow curve above, where understanding is very limited until you've read almost all the code, at which point it expands very quickly.
14+
Most programming languages are not modularly comprehensible in such a linear fashion but in an exponential one, as illustrated with the yellow curve. Understanding is very limited until you've read almost all the code, at which point it expands very quickly.
1515

16-
The lack of modular comprehensibility slows down the time it takes a programmer to make a change to an unfamiliar project. This is particularly relevant in open-source software, because developers have limited time to contribute. Have you ever wanted to make a small bug-fix or improvement to an open-source project, but gave up after a few hours of failing to understand how the code works?
16+
The lack of modular comprehensibility slows down the time it takes a programmer to make a change to an unfamiliar project. This is particularly relevant in open-source software, because developers have limited time to contribute. Have you ever wanted to make a small bug-fix or improvement to an open-source project, but gave up after a few hours of failing to understand how the code works?
1717

18-
## Module dependencies
18+
## Data dependencies
1919

20-
Code is not modularly comprehensible because the way dependencies between modules are organized.
20+
Most code is not modularly comprehensible because the way data dependencies between modules are organized.
2121

22-
In [Reactive MVC and The Virtual DOM](https://web.archive.org/web/20180530055638/https://futurice.com/blog/reactive-mvc-and-the-virtual-dom), Andre Staltz discusses the trade-offs between the Interactive (or imperitive) pattern and Reactive patterns.
22+
In [Reactive MVC and The Virtual DOM](https://web.archive.org/web/20180530055638/https://futurice.com/blog/reactive-mvc-and-the-virtual-dom), Andre Staltz discusses the trade-offs between the Interactive and Reactive patterns.
2323

2424
> ![image](https://user-images.githubusercontent.com/2288939/42631117-35144a28-85a7-11e8-877e-552732396590.png)
2525
>
@@ -41,41 +41,89 @@ In [Reactive MVC and The Virtual DOM](https://web.archive.org/web/20180530055638
4141
>
4242
> The benefit of Reactive over Interactive is mainly separation of concerns. In Interactive, if you want to discover what affects X, you need to search for all such calls `X.update()` in other modules. However, in Reactive, all that it takes is to peek inside X, since it defines everything which affects it. For instance, this property is common in spreadsheet calculations. The definition of the contents of one cell are always defined just in that cell, regardless of changes happening on the other cells it depends on.
4343
44-
Note on the word "module": In his example above, Staltz uses JavaScript files as representative of modules, but you could replace the `foo.js` filename with `foo` the variable or function name for similar result. Modular comprehensibility has nothing to do with browserify modules. The dictionary definition of module is relevant: "any of a number of distinct but interrelated units from which a program may be built up or into which a complex activity may be analyzed."
44+
Note on the word "module": In his example above, Staltz uses JavaScript files as representative of modules, but you could replace the `foo.js` filename with `foo` the variable name for a similar result. Modular comprehensibility has nothing to do with browserify modules. The dictionary definition of module is relevant: "any of a number of distinct but interrelated units from which a program may be built up or into which a complex activity may be analyzed."
4545

46-
I believe that Staltz understates the non-modularity of the Interactive pattern. Let's say you wish to understand the behavior of X, so you grep for `X.update` in all modules. For each module `X.update` appears in, you have to go to the call site(s) and understand that module well enough to know what triggers that line of code, and arguments it will pass in. If you're lucky, all that information is self-contained, but you'll likely be forced to understand yet more modules to understand how *this* module affects X.
46+
Staltz understates the non-modularity of the Interactive pattern. Let's say you wish to understand the behavior of X. You grep for `X.update` in all modules. For each module `X.update` appears in, you have to go to the call site(s) and understand that module well enough to know what triggers that line of code, and the value of the arguments it will pass in. If you're lucky, all that information is self-contained, but you'll likely be forced to understand yet more modules to understand how *this* module affects X.
4747

48-
The modular comprehensibility of a language is a function of how explict or implicit data dependencies are between modules. The Interactive pattern, which is representative of JavaScript, can never be modularly comprehensible, because data dependencies are hopelessly implicit.
48+
The Interactive Pattern, representative of most JavaScript UI frameworks, is not modularly comprehensible, because data dependencies are implicit, because the language permits side-effects, such as mutable state, which allows `bar.updateSomething(someValue);`.
4949

50-
The Reactive Pattern enforces explicit dependencies, which are a huge win for modular comprehensibility. For example, consider the program `A` below, which is explicitly defined in terms of its modules below. In this picture read an arrow from B to A as "A is defined in terms of B" or "A explictly depends upon B."
50+
The Reactive Pattern is modularly comprehensibility because it enforces explicit dependencies, by disallowing side-effects.
51+
52+
Consider the program `A` of the Reactive Pattern, which is explicitly defined in terms of its modules. In this picture read an arrow from B to A as "A is defined in terms of B."
5153

5254
<iframe width="500" height="300" src="https://mermaidjs.github.io/mermaid-live-editor/#/view/eyJjb2RlIjoiXG5ncmFwaCBCVFxuICAgIFxuQi0tPkFcbkMtLT5BXG5ELS0-QVxuRS0tPkFcbkYtLT5CXG5HLS0-QlxuSC0tPkJcbkktLT5DXG5KLS0-Q1xuSy0tPkRcbkwtLT5FXG5NLS0-RVxuTi0tPkVcbk8tLT5FXG5LLS0-TlxuUS0tPk5cbiIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In19" frameborder="0" allowfullscreen></iframe>
5355

54-
If you want to understand a module, you have to understand it's modules, recursively. So if you want to understand the entire program, A, you have to read everything. However, let's say you were only interested in module E. In this case you only have to read the E and it's children, recursively, highlighted below:
56+
If you want to understand a module, you have to understand the modules it's defined in terms of, recursively. So if you want to understand the entire program, A, you have to read everything. However, let's say you were only interested in module E. In this case you only have to read the E and it's children, recursively, highlighted below:
5557

5658
<iframe width="500" height="300" src="https://mermaidjs.github.io/mermaid-live-editor/#/view/eyJjb2RlIjoiXG5ncmFwaCBCVFxuICAgIFxuQi0tPkFcbkMtLT5BXG5ELS0-QVxuRS0tPkFcbkYtLT5CXG5HLS0-QlxuSC0tPkJcbkktLT5DXG5KLS0-Q1xuSy0tPkRcbkwtLT5FXG5NLS0-RVxuTi0tPkVcbk8tLT5FXG5LLS0-TlxuUS0tPk5cblxuY2xhc3NEZWYgY2xhc3NOYW1lIGZpbGw6eWVsbG93LHN0cm9rZTojMzMzLHN0cm9rZS13aWR0aDo0cHg7XG5jbGFzcyBFLEwsTSxOLE8sUSxLIGNsYXNzTmFtZTtcbiIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In19" frameborder="0" allowfullscreen></iframe>
5759

58-
Explicit data dependencies allow you to comprehend modules by reading their definitions, recursively. This allows you to *categorically rule out all the modules you do not have to read* in order to comprehend the relevant module. In the above example, that's all the modules that are not highlighted. If a module is not an explicit dependency (or dependency of a dependency...), it's not relevant. In fact, it's explicitly *independent*.
60+
This allows you to *categorically rule out all the modules you do not have to read* in order to comprehend the relevant module(s). In the above example, that's all the modules that are not highlighted. If a module is not an explicit dependency (or dependency of a dependency...), it's not relevant. In fact, it's explicitly *independent*.
61+
62+
## Disallowing side-effects: only pure functions
63+
64+
The Reactive Pattern enforces explicit dependencies disallowing side-effects. As defined in Wikipedia, a side-effect "modifies some state outside its local environment or has an observable interaction with the outside world."
65+
66+
Let's address the first part of that definition, "modifies some state outside its local environment". Removing side-effects disallows calls like these:
67+
68+
> `bar.updateSomething(someValue);`
69+
70+
As discussed above, the problem with this code is that it can live outside of `bar`, which makes it really difficult to comprehend `bar`s behavior without reading all the modules that affect it as well (and all the modules that affect those).
5971

60-
## Restricting the model
72+
We can go even further and disallow *all* mutable state, leaving us with only pure functions, and a langauge in the spirit of Haskell.
6173

62-
The Reactive Pattern enforces explicit dependencies by restricting the programming model, disallowing side-effects, such a mutable state, leaving us with only pure functions, and a langauge in the spirit of Haskell.
74+
Next, let's address the second part of the definition, "has an observable interaction with the outside world." Pure functions work beautifully for batch software that accept an input, do some internal computation, and return an output, such as a compiler.
75+
76+
However, programming is ultimately about building software that *affects the world*. Ultimately our software is going to have to *do things*: move bits, open files and sockets, send HTTP requests. How can pure functions represent all that?
77+
78+
The accepted answer to this question is: they can't. Haskell is split into two worlds: the world of pure expressions and the world of the `IO ()` monad. Life isn't worth living without getting and putting characters to the terminal:
79+
80+
```
81+
getChar :: IO Char
82+
putChar :: Char -> IO ()
83+
```
6384

64-
This works beautifully for batch software that accept an input and return an output, but does not scale to interactive software that responds to inputs over time. That's where FRP comes in.
85+
Just kidding. It's 2018. Who's writing terminal apps? Creating modern user interfaces has very little to do with getting and putting characters on the screen on-by-one.
6586

6687
## FRP
6788

68-
"Functional Reactive Programming is a way of writing interactive software using only **pure functions**" [[Ryan Trinkle](https://youtu.be/dOy7zIk3IUI?t=1m27s)]
89+
The popular way to create UIs today is inspired by Functional Reactive Programming. FRP is a way of declaratively describing interactive UIs *with only pure functions* - no monads required.
6990

70-
FRP is particularly useful for constructing software interfaces. The popular UI framworks of today, such as ReactJS, are inspired by FRP principles - but without entirely forgoeing side-effects.
91+
You *declaratively describe what the UI should look like as a function of state*, as opposed to imperitively adding and removing characters to the screen one-by-one. (Of course, there's code somewhere that's adding and removing characters from the screen, possibly in the form of a "virtual dom", but we don't have to worry about that - it's below FRP's level of abstraction.)
7192

72-
behaviors and events (streams)
93+
Let's make a button that counts its clicks. The UI is a simple pure function from the number of clicks to the button's HTML:
7394

74-
I like to think about FRP as "zooming out." code is organized around pieces of data, and events are subordinated as explicit dependencies of data as infinite streams.
95+
```javascript
96+
function view(count) {
97+
return `<button id='counter-button'>${count}</button>`
98+
}
99+
```
100+
101+
But how do we calculate the counts? I have two helpful metaphors for FRP: "zooming out" and "inverting control". Normally, you deal with events one by one, and the code is organized in terms of events. "When an event occurs, run the following code."
102+
103+
```javascript
104+
var count = 0
105+
document.getElementById("counter-button").onclick = e => {
106+
count++
107+
}
108+
```
109+
110+
But let's zoom out. Instead of dealing with events one-by-one, let's consider all the events that will ever happen as infinite lists (streams) of events. Next, let's define our data in terms of these streams, so that the data is now top-level and events are subordinated as dependencies of data.
111+
112+
```javascript
113+
const count = document.getElementById("counter-button").clicks
114+
.reduce((accumulator, currentValue) => accumulator + 1)
115+
```
116+
117+
You may be wondering how `count` can be a `const`, even though it will update as the button is clicked. This is because `count` is itself a stream of values that can be used as a dependency in streams defined elsewhere.
118+
119+
TODO:
75120

76121
space time issues
77122

78-
# The Elm Architecture
123+
While the popular UI framworks of today, such as ReactJS, are inspired by FRP principles, they do not
124+
125+
126+
## The Elm Architecture
79127

80128
However, most web-based FRP frameworks share a data model that does not feature modular comprehensibility. The data model they share was originally concieved as for the Elm programming language, a pure langauge in the spirit of Haskell, and entitled "The Elm Architecture." It inspired ReactJS's Redux, VueJS's Vuex, CycleJS's Onionify, among many others.
81129

@@ -197,6 +245,7 @@ Thank you Jonathan Edwards for you continued mentorship.
197245

198246
* title ideas
199247
* FRP and Explicit Data Dependencies
248+
* put Staltz in my own words?
200249
* Reflex TodoMVC?
201250
* find a real life open-source UI app as an example?
202251
* https://github.com/rtfeldman/elm-spa-example

0 commit comments

Comments
 (0)