
The Misunderstood Roots of FRP - ecbu
https://futureofcoding.org/essays/dctp
======
pubby
Yeah okay. Then why does nobody use it? Even languages like Elm ditched it for
easier to understand concepts.

Let's face it. FRP kinda sucks. It's awkward as hell to use and takes way to
many brain-cells to achieve simple results. Even the experts have trouble with
it. I remember e-mailing the author of a popular FRP library once asking him
how I could get multiple shapes moving on the screen at once (as opposed to
just one). He replied that he had no idea. Apparently it was still an open
problem he was working on. Yikes!

State turns out to be a convenient, natural way to represent... state. Funny,
isn't it? That if you have some real world problem that involves state, that
you want to model that using virtual state and not the high-order time-varying
continuous functions that FRP uses. Even Alonso Church admitted that Turing's
tape-based model of computation was easier to understand than his lambda
calculus.

BTW one more thing. Spreadsheets are commonly cited as a good example of real-
world FRP use. The great irony is that nobody on the planet thinks of
spreadsheets as time-varying continuous functions. Nope. People view
spreadsheets as a big grid of numbers... i.e. state.

~~~
proc0
Isn't Redux FRP? You can easily do the basics in React as well. I would say
it's the dominant paradigm in UI development and most don't know they're using
it.

~~~
nh2
Having worked with both Redux and a "real" FRP implementation (Haskell's
Reflex library), I wouldn't call Redux FRP.

In Redux one explicitly, imperatively sends messages to a top-level message
dispatcher, which then invokes more imperative code.

This feels /very/ different to Reflex's folding over events, and transforming
dynamic values with pure functions.

The API you use is pretty different as well; consider:

* [https://github.com/reflex-frp/reflex/blob/develop/Quickref.m...](https://github.com/reflex-frp/reflex/blob/develop/Quickref.md)

* [https://github.com/reflex-frp/reflex-dom/blob/develop/Quickr...](https://github.com/reflex-frp/reflex-dom/blob/develop/Quickref.md)

~~~
proc0
Completely agree Reflex is proper FRP. Redux does have some of the basics of
FRP and you can stick to pure functions as well, which is limited by JS but
not necessarily by the Redux pattern. I believe Redux is subset of FRP, that
uses discrete commands instead of continuous function of time, and the time
function can be made into descrete events for implementing commands. The rest
builds on top of that.

------
csande17
Imagine you were trying to explain the temperature-conversion program's
behavior in the simplest, clearest way possible. (That is, after all, what
programmers fundamentally do: try to explain things to computers as simply and
clearly as possible.) You'd probably say something like: "When the user
changes the Celsius textbox, the computer changes the Fahrenheit textbox to
the converted value." And indeed, that's the program most people would write:

    
    
        celsiusTextbox.whenChanged do
            fahrenheitTextbox.value = cToF(celsiusTextbox.value)
        end
    

What you _wouldn 't_ say is something like: "The Fahrenheit textbox's value
depends on whether the Fahrenheit textbox or the Celsius textbox was edited
more recently. If the Fahrenheit textbox was edited more recently, its value
is equal to the value the user entered into it most recently. If the Celsius
textbox was edited more recently, the Fahrenheit textbox's value is equal to
the converted value of the value the user entered into the Celsius textbox
most recently." But that's the program you have to write in the "DCTP"
approach proposed by the author.

I think this is true of a lot of UI programming: most people intuitively think
about it in terms of "when X happens, do Y", so trying to cram it into another
programming model is more trouble than it's worth.

~~~
throwaway_n
Won't your example trigger an infinite loop of celsius/fahrenheit conversions?
Especially if the floating point value is slightly off.

I think your example is exactly why stuff like React won vs data-binding
frameworks. With declarative programming, you just have a single source of
truth that you change once (you can arbitrarily pick celsius or fahrenheit or
even kelvin) and let the framework figure out what needs to be diff-ed in the
derived views.

~~~
csande17
I'm not aware of any actual real-world GUI system where ".whenChanged do" is
valid syntax, but just for the sake of argument, pretend that it only fires
when the _user_ changes the textbox. Coincidentally, this also happens to be
how the "change" event works in JavaScript.

~~~
Izkata
I know of one that's essentially like that [0], but like you suggested - it
only fires on user action, not programmatic update.

(The examples here are for buttons and sliders, but it's the same syntax for
input fields)

[0] [http://rebol.com/docs/easy-
vid.html#section-20](http://rebol.com/docs/easy-vid.html#section-20)

------
dustingetz
What Steve calls "Denotative Continuous-Time Programming" – I think Clojurists
call this declarative data programming – that is, you figure out a way to
model the problem domain in data, and then write pure functions on that data.
For example, React.js models the dom as data. Clojure gives you the toolkit of
programming language primitives you need to do this easily, ergonomically and
often, for all of your problem domains, not just virtual dom.

I think an example of database programming in "Continuous Time" would be

    
    
        (datomic.api/q '[:in $ :find ?e :where [?e :post/title]] db)
    

where `db` is a time-pinned and consistent value of the database graph – in
other words datomic.api/q is a conceptually pure function of time, time is a
parameter, which implies you can rewind it or speculate into the future (both
of which are supported by this interface)

As for abstracting HTTP – what if you considered network io as just a way to
lazy load a cache of immutable database values? So for example, this would
work a bit like Git – we don't care how the clone protocol works, just load me
the file values I identified. Maybe it uses HTTP, maybe it uses something
different, who cares? Get me the value I asked for in the fastest way possible
given available infrastructure, distributed caches, etc.

Then, the question of IO resolves to: what categories of effects can be
modeled as values and functions on values? Given declarative data programming
in Clojure – basically anything!

~~~
skybrian
You can think of a timeline as immutable, but you don't know values from the
past unless you arranged for them to be recorded, you don't know values from
the future until they happen, and you don't know what's going on at any other
node unless they send you a message about it and it eventually makes it across
an unreliable network. (So, sometime after it actually happened.)

If you model this in a declarative way then you have to be careful to avoid
implying that all events should be remembered, and also avoid depending on
anything in the future unless you want to wait for it. This makes designing an
intuitive FRP-based language pretty hard.

~~~
dustingetz
> be careful to avoid implying that all events should be remembered

Is that true in a post-AWS world with storage getting cheaper faster than you
can consume it? Maybe don't put 4k video in the log. But, you're right, we can
stream, shard and forget as necessary.

> also avoid depending on anything in the future unless you want to wait for
> it

Can you elaborate on this, my gut reaction is to ask why I need to avoid
depending on git commits that haven't been written yet – it's kind of a weird
question right? Time is explicit now, which means you have the right knobs you
need to coordinate it, even across distributed nodes.

~~~
csande17
> Is that true in a post-AWS world with storage getting cheaper faster than
> you can consume it?

This might be true of "storage" as in disk space, but it definitely isn't true
of "storage" as in RAM. If your phone kept an in-memory log of every single
click event, you'd run out of RAM pretty fast.

------
macintux
Fascinating content. As someone burdened by working in a wildly non-denotative
language (Python) but who would love to return to functional programming
someday, the vision is very appealing.

I like FP because it allows me to be lazy; quoting the author:

> A denotative language resembles a dictionary or encyclopedia, where one can
> understand an entry by reading it and what it references. A non-denotative
> language resembles prose, like a novel, which you have to read cover-to-
> cover to know what happens, even if you only care about one specific
> character.

------
dmitriid
As with most if these things, the theory is fascinating. And then comes a
contrived small code example... and the theory falls apart:

\- the example is trivial

\- the code example is extremely complicated for such a trivial example

\- the code has to be explained in minute detail, with at least one
visualization (better, two)

\- and it still remains largely over complicated

I shudder to think of any non-trivial example with this approach. It will
hardly be more comprehensible than existing RxJS or Redux code.

~~~
zenhack
I kindof wonder if this isn't one of those things that falls flat _because_
it's a toy example, and anything but the most direct approach is going to look
clumsy and over-engineered.

I've spent a bunch of time working in Elm, though I hadn't used it in anger
before they dropped the FRP stuff.

My experience with post-frp Elm is that:

* It seems really elegant on small examples * When you start working with larger codebases, and you have some resuable UI elements you want to build, you end up writing a lot of "routing" code to shunt messages to sub-components. Conventional wisdom in the community is to try to keep app structure as "flat" as you can to avoid this, but I've not seen a codebase of meaningful size where this doesn't happen enough to be annoying.

I have a gut instinct that "real FRP" might shine a bit more at this point; it
seems like it would make wiring together different bits of the UI easier.

------
skybrian
Saying that expressions independently denote a value is misleading. You can't
know what an expression means without knowing the definition of each symbol it
uses, and those symbols come from the expression's environment. These symbols
may be defined in terms of other symbols, in turn, and the dependency graph of
a large program is by no means simple.

Examples that use well-known mathematical functions give a misleading
impression that such expressions will be easy to understand. Instead, it can
become like puzzling over the meaning of the equations in a mathmatics paper.
As the author discovered, this isn't always easy, particularly for unfamiliar
mathematical objects, and it tends to appeal more to people with a background
in mathematics.

Also notice that the focus on the _value_ of an expression hides all
performance issues. Maybe we can specify what an animation should do, but that
doesn't mean it will run smoothly. It can be valuable to cleanly separate so-
called "correctness" from performance (as if a program that's too slow is
somehow correct?), but this doesn't relieve the programmer of the
responsibility to work on performance. Languages that don't give you the tools
to control performance are incomplete.

------
aryehof
I have long thought that the Nygaard definition of a functional _program_ was
correct, but largely misunderstood.

    
    
       "A functional program is regarded as a mathematical function, describing a relation between input and output."
    

I think this article is helpful in explaining how insightful that definition
is. It's interesting to contrast that with his other classifications of major
paradigms at that time. Particularly that of an Object Oriented program (he is
one of the fathers of OO with Dahl), which is at odds with the modern
viewpoint today ...

1\. ProceduralProgramming. A program execution is regarded as a (partially
ordered) sequence of procedure calls manipulating variables.

2\. ConstraintProgramming. A program is regarded as a set of equations
describing relations between input and output.

3\. ObjectOrientedProgramming. A program execution is regarded as a physical
model, simulating the behavior of either a real or imaginary part of the
world.

------
perl4ever
I notice the mention of spreadsheets.

Something I never thought about until I started writing scripts in Excel is
that when you define new functions to be used in cell formulas, they are not
allowed to have side effects...and it didn't take long before I _wanted_ to
write functions with side effects.

Sure, this makes sense from a certain perspective, but when you're using a
dialect of Basic, you kind of think it's down and dirty, anything goes. I
mean, this is the language that used to usually have "peek" and "poke".

~~~
falcor84
For better or worse, Google Sheets functions (written in App Script) are
allowed to have side effects. Though when invoked in cells, you don't have
full control over when they're actually recalculated (there seems to be
complex caching), so you need to plan your side effects accordingly.

------
zyxzevn
Nobody quickly understands:

    
    
        A(B(C(D(E(x)))))
    

That is why they use the assignments.

    
    
        e= E(x)  
        d= D(e)  
        c= C(d)  
        b= B(c)  
        a= A(b)
    

But many people can understand:

    
    
        x-> E -> F-> C-> B-> A  
        // where -> is a pipe operator.
    

Now you see that you first execute E with x as input.

This shows that Functional program has some kind of problem, if it is not
presented in a good way.

There are also other problems. The overuse of Currying is bad, as it hides
what is going on. Then we can also have recursion mixed with lazy execution.

I think that the problem with functional programming is the bad presentation
of what is really going on.

I try to overcome these problems by using a pure graphical system instead.
Everything should be as simple and clear as possible. The system is still in
design/development, but you can see some at
[http://www.reddit.com/r/unseen_programming/](http://www.reddit.com/r/unseen_programming/)

The general idea is that cells like in a spreadsheet are a basis for our
functions. Unlike a spreadsheet-cell they can contain multiple variables.
These cells can then be combined with pipes and streams. That may already
define an functional language, but for me that is just the start.

~~~
pasquinelli
The first line seems the most easily parsed to me. The second one is the
worst, and the third one only works for a tidy pipeline; if you have to
combine multiple pipelines you'd need to bracket them, as in the first.

~~~
zyxzevn
You must be used to reading inside out, instead of left to right.

The parameters will mess up as soon you have multiple parameters. Especially
messy if you want to reuse them. Or when you have conditions.

In a graphical system you do not need brackets, which is why I added it as a
possible solution.

~~~
Izkata
The first and third are both easier for me than the second.

At least in my case, it's not an inside-out or left-right thing, it's a funnel
that shows the exact relationship of the data. The problem with the second one
is that the multiple distinct statements require scanning up and down across
them to ensure the local variables aren't used anywhere else, and then
mentally reconstructing the data's flow from one function to the next.

It's also why I strongly prefer statements like this:

    
    
      A(
        B(C),
        D,
        E(F(G))
      )
    

The flow of data is encoded directly in the visual structure, instead of being
split out across various statements using local variables that leak
intermediate state and introduce accidental mental overhead.

~~~
tsimionescu
But the second form is also more general, since you can express any
computation that way, whereas you can't in the first or third forms.

For example:

    
    
        x = A(B)
        y = D(x) 
        z = C(x, E(y)) 
    

Is either an important optimization (possibly affecting asymptotic complexity
in more complex cases), important for correctness (if any of the functions
have side-effects), or at least more succint than the alternatives (in a lazy
language like Haskell).

------
emmelaich
For the author - it's "Salon de Refusés" not refuge.

i.e. "exhibition of rejects"

[https://2019.programming-
conference.org/track/sdr-2019-paper...](https://2019.programming-
conference.org/track/sdr-2019-papers)

------
tsimionescu
The author comments often that there are certain concepts that 'belong in the
implementation, not the surface', such as explicit sequences of operations and
any interaction with the outside world.

I believe that this relegates DCTP to the status of a domain-specific
language, not a general programming approach: if we know from the start that
there are certain computations that we can't/don't want to perform with DCTP,
then we know from the start that it only applies to certain domains of
programming.

This isn't a problem in itself of course - DSLs can be wonderful things. But
this can become a problem if you haven't taken the time to define the domain
where your DSL is useful, or if that domain turns out to be extremely narrow.
Spreadsheets are a wonderful abstraction for some things, but you wouldn't
build an OS with them, nor even a simple web app.

Perhaps they can extend the abstraction until it can become 'nearly general-
purpose' (after all, you wouldn't really write an OS in Java either), but I
don't think that's guaranteed.

------
dakom
I spent a fair amount of time dabbling with FRP - for example, wrote a Pong
for the web using SodiumFRP and Purescript[0]

imho FRP is absolutely elegant and wonderful when it all comes together. The
difficulty, I found, is iterating and hacking away in order to discover what
the end result was meant to be all along. In that respect FRP does slow down
development time (at least in my hands. Maybe those with more expertise could
iterate faster).

I'd imagine that even in that scenario it could be fruitful to use something
less robust for prototyping, and then implement it with FRP for a maintainable
release version that's easier to reason about.

[0] [https://github.com/dakom/frpong](https://github.com/dakom/frpong)

~~~
iamwil
What did you find that you had to hack away? Was it notions of how to do
things from your previous experience as a programmer? Or was it more specific
to the domain problem at hand?

If it's the latter, then I'd argue that's part of the exploration of the
problem you're trying to solve, no?

~~~
dakom
Hmmm good question! I can't say for sure - I guess it was a bit of both.

In either case, I found it's faster to play around with changes in other
approaches, though another way of phrasing that could be that it's easier to
break things too :)

------
z3t4
I know you like to write HTML. But stop doing it! So how do you define view
state without HTML? You use functions! Then you can use any paradigm you like.
And you get performance. Static sites and static site generators is still a
thing, but it works very different from a client side app. Then we have the
in-between with an app that renders a static view on the server. But you have
to stop writing client side apps like if they where server rendered.

------
jonstewart
What’s FRP?

~~~
rbobby
I had the same question. Functional Reactive Programming.

~~~
vortico
Thanks. I'll recite the rule of acronyms and articles: _Always define all
acronyms in your article._

------
sriku
This stuff is close-to-heart for me as FRP (and Elliott's work in general)
greatly inspired my thinking over the years. Here are a few instances where
the drive to be denotational in api design led to very satisfactory results.
This is a massive "shameless plug" post, but I justify it as a dedication to
Conal Elliott.

First, a couple of old talks on the topic -

\- "Functional thinking" \- Brings together some of the thinking I've applied
in a few areas - [http://sriku.org/blog/2015/08/11/talk-functional-thinking-
fo...](http://sriku.org/blog/2015/08/11/talk-functional-thinking-for-fun-and-
profit/) .

\- "Beta abstraction for bottom-up theory building" \- Shows how beta
abstraction can be applied to systematically _become_ denotational -
[http://sriku.org/blog/2016/02/06/beta-abstraction-for-
bottom...](http://sriku.org/blog/2016/02/06/beta-abstraction-for-bottom-up-
theory-building/)

Systems -

\- In "muvee Reveal", an automatic video production system, the styles are
written in a Scheme-dialect called "muSE"[1] in which stuff needed to
represent stylistic elements are built. The editing styles DSL [2] and
documentation show how this stuff can be represented well removed from
implementation details. (Disclaimer: I used to work for muvee, but no longer
do so stuff may have changed.)

In particular, video and animations were not modeled as functions of time to
Image, but functions of a time interval [t,t+dt] to Image. The reasoning was
that the interval information is critical to render motion blur. You could
argue that the "dt" is a detail that can be passed on later at render time,
but it didn't take away much from the API's simplicity.

\- Steller[3] - a library for declaratively composing dynamic temporal
structures, useful (and used) for music and synchronized animations.

\- elm-anima[4] - a concept demo of structuring animations in Elm. This was
pre-18 and so doesn't use subscriptions. Here, animations are conceived of as
processes rather than functions over time. I've described the thinking in a
post [5]. Elm fell out of favour for me as there were many APIs I needed to
work with that I couldn't use it with and the portion that needed Elm was
small in the systems I was working with.

[1]: [https://github.com/srikumarks/muSE](https://github.com/srikumarks/muSE)

[2]: [https://srikumarks.github.io/muvee-style-
authoring/](https://srikumarks.github.io/muvee-style-authoring/)

[3]:
[https://github.com/srikumarks/steller](https://github.com/srikumarks/steller)

[4]: [https://github.com/srikumarks/elm-
anima](https://github.com/srikumarks/elm-anima)

[5]: [http://sriku.org/blog/2015/12/13/towards-reactive-
animation-...](http://sriku.org/blog/2015/12/13/towards-reactive-animation-in-
elm/)

~~~
iamwil
What did you replace Elm with in your later work with FRP systems?

Also, with your Elm-anima project, what were the lessons and takeaways? It
seemed like it was working, but what are the limitations of the approach?

~~~
sriku
I wrote a small JS "framework" that's not even worth that name, but which
works well enough by decoupling components CSP style. The HTML rendering part
was not the most complex piece in the system (it was webaudio code, wasm, etc.
dominated) to warrant dependency on a language so the additional complexity
wasn't worth it.

Elm-anima - yes I felt that the elm-anima approach worked fine conceptually
and is fairly performant too with scope to inject caching and laziness, but
its approach ran into 0.18 's subscription mechanism (it was done before
subscriptions) and I couldn't wrap my head around the loss of control there
for this purpose. Things didn't compose after that.

Edit: elm-anima also needs more work to support effects, without burdening the
API too much, to be truly useful.

Personally, I'd have liked the whole program in Elm to be modeled as just an
"Automaton Input (Html, Task x ())" or something. That would've given enough
freedom to do these kinds of things and, with some effort, be performant too.

------
mathgladiator
The problem here is FRP is still immature and produces too much toy-ness. I've
done some work in space, but the core of it is that the techniques are too
expensive at the moment. This expense manifests in poor capacity utilization
in the data center, and poor battery usage in mobile devices.

------
m4r35n357
Even the article doesn't explain what FRP is . . .

