Lex Huang, June 2018
Here is a simple introduction to
Functional Reactive Programming (FRP), we won't go deeper into the mathematical theories behind
the paradigm or explain the different implementations or types of it.
Note that FRP fits some problems better than others. User interfaces and networking are two event-based
areas where it fits especially well. Not surprisingly, FRP is excellent for web applications. For
other situations, other paradigms like actor model would be more suitable.
How to enhance progressively from left to right?
Simple things taking too long.
Rewritten from scratch!
And a million dollars later...
It hits the same wall again!
The old school state machine works in the following way:
We could say that all programs are fundamentally state machines.
codes written in a traditional state-machine style tends to be unreadable and brittle.
codes become hard to reason about as the program goes bigger.
And a programmer’s main task is to structure clean codes.
KEEP CALM and REBOOT.
CTRL+ALT+DELETE fixes everything.
If all else fails, RESTART.
Still not work?
Restart the world!
A specific method of reactive programming that enforces the rules of functional programming, particularly the property of compositionality.
Conal Elliott is one of the inventors of FRP, this concept was first introduced in his paper Functional Reactive Animation to composing richly interactive, multi-media animations.
Cells: Continuous values over time
Streams: Discrete events pushed into the FRP sys.
Declarative combinators that guarantee sensible composition.
const num = new Cell<number>(1);
const doubled = num.map(n => n * 2);
// <Cell<A>>.lift( b: Cell<B>, (A, B) => C): Cell<C>
const added = num.lift( doubled, (n, d) => n + d);
Always has a value
const nums = new StreamSink<number>();
const doubled: Stream<number> = num.map(n => n * 2);
const evens: Stream<number> = num.filter(n => n % 2 === 0);
nums.send(2);
Fires at discrete points in time with a value.
FRP is a subset of both functional and reactive programming.
A broad term meaning that a program is
It doesn’t dictate any specific method of achieving these aims. Reactive programming gives looser coupling between program components, so the code is more modular.
Microsoft’s Reactive Extensions ( Rx) is mostly concerned with chaining event handlers, and it gives you many options for how you do it.
A style or paradigm of programming based on functions, in the mathematical sense of the word. It deliberately avoids shared mutable state, so it implies the use of immutable data structures, and it emphasizes Compositionality and Referential transparency
The principle of compositionality:
The meaning of an expression is determined by the meanings of its parts and the rules used to combine them.
Without compositionality, the consequences of composing program modules are ad hoc.
FRP is compositional because it imposes mathematical rules that force the interaction of program elements to be simple and predictable by using denotational semantics . So we can think in teams of dependency and think declaratively.
Referential transparency is an oft-touted property of (pure) functional languages, which makes it easier to reason about the behavior of programs. I don't think there is any formal definition, but it usually means that an expression always evaluates to the same result in any context. Side effects like (uncontrolled) imperative update break this desirable property. C and ML are languages with constructs that are not referentially transparent.
In short, the function must have no external effects other than through the returned value, and it must not be affected by any external state.
Traditionally, software is expressed as a sequence of steps.
The problem with representing dependencies as a sequence comes when you go to change the code.
How to cook a lasagna?
Nope! This is no way to write a cookbook:
it’s an
operational definition of lasagna
This is how our cookbook would be written:
Notice a few things:
vs
If you fix the problems with listeners, you’ll invent FRP.
Typically during program startup, FRP code statements are converted into a directed graph in memory.
For the rest of the program execution you feed in values and turn the crank handle, and the FRP engine produces output.
Data Type | Outputs a Stream | Outputs a Cell | Outputs a value |
---|---|---|---|
Stream | |||
Cell |
class Stream<A>
Stream represents a stream of events.
class Cell<A>
Cell is a mutable variable——because it represents the state mutation
In a similar way, FRP takes the evil of event handling, divides it into little pieces, and then banishes each piece into an evil-proof box called a stream or a cell.
never :: Stream a
In Sodium it doesn’t have the name never. If A stream constructed in the way has no mechanism to cause it to fire, so it’s guaranteed never to fire.
map :: (a -> b) -> Stream a -> Stream b
merge :: Stream a -> Stream a -> Stream a
Two or more stream events that occur in the same transaction
But No.3 on the left, a single mouse click will cause two simultaneous events to be generated:
You’ll almost certainly want to merge these streams at some point in the program. Because these two events originate in the same mouse click event, they’re simultaneous.
Otherwise, you will get a glitch of the cursor style.
The mechanics of how merge deals with simultaneous events
orElse :: s1.merge(s2, (l, r) -> l)
snapshot :: Stream a -> Cell b -> Stream (a, b)
filter::Stream (Maybe a) -> Stream a
hold :: a -> Stream a -> Cell a
let's go back to the last example
Can you see the loop here?
Value loop—In functional programming, a value defined directly or through other variables in terms of itself.
So how do we do?
A dummy!——CellLoop and the loop operator
lift ::(A -> B -> C) -> Cell A -> Cell B -> Cell C
map on cells can be seen as a lift of a single cell.
sample ::Cell A -> A
This is useful for interacting with non FRP logics.
Allows the data flow graph to change dynamically
switchC :: Cell (Cell A) -> Cell A
switchS :: Cell (Stream A) -> Stream A
Interfacing your FRP code to the rest of your program has two parts:
A subclass of Stream that adds a method called send()
A subclass of Cell that has send() and listen()
create a transaction explicitly like this:
Sodium executes a transaction in two steps:
Multiple send()s in a single transaction
Packets come into the system,each containing a list of commands.
Let’s split the packets into individual commands.
we can also nested the split
"If a tree falls in a forest and no one is around to hear it, does it make a sound?"
Not in the FRP world!
FRP's restriction:
What you’re getting in return?
OOP vs FRP
with drag&drop exampleThe mechanism of continuous time is to update a cell representing time before passing external events into the FRP system.
Externally, you’re saying, “Please sample the model at time t,” but within the model, you can think of time as varying continuously.
such as
Features
Features(continue)
Features(continue)
Features(continue)
The interface between the pump logic and the (simulated) outside world
modules:
Last thing, don't forget to put them together, make an frp directed graph.
module example: keypad logic
I’m glad you’re starting by asking about a specification rather than implementation first. There are a lot of ideas floating around about what FRP is. For me it’s always been two things: (a) denotative and (b) temporally continuous. Many folks drop both of these properties and identify FRP with various implementation notions, all of which are beside the point in my perspective.
By “denotative,” I mean founded on a precise, simple, implementation-independent, compositional semantics that exactly specifies the meaning of each type and building block. The compositional nature of the semantics then determines the meaning of all type-correct combinations of the building blocks
A true FRP system has to be specified using denotational semantics. FRP controls what you do more tightly and gives you strong guarantees in return.
The old FRP space leaks, because it try to remember every thing in the past.
It can still be possible in Sodium when you change the scence graph not carefully enough.
But if you forget the past, you can still make your computer happy: Practical Principled FRP.
About sodium:
Essays:
Demos:
Other materials: