paldepind / functional-frontend-architecture

A functional frontend framework.
MIT License
1.44k stars 87 forks source link

flimflam - yet another approach #23

Open jayrbolton opened 8 years ago

jayrbolton commented 8 years ago

I have spent some months updating and refining an approach to an FRP driven pattern that I'm calling "flimflam". Main points:

Examples:

jayrbolton commented 7 years ago

Enjoyed the blog post @paldepind, many thanks

dmitriz commented 7 years ago

@paldepind

Great post and well written again! I had been sceptical myself and the post addresses some of my concerns very well. Perhaps it will be worth publishing it on Medium and see how the broader community reacts.

I somehow feel puzzled by what is really happening with the FRP in JavaScript community, it nearly seems like the major frameworks act as if the FRP does not exist. I have tried to document it here, and the prevalence of old imperative style is stunning, to say the least.

I came accross interesting projects like this one that feel abandoned and forgotten: https://github.com/jas-chen/thisless-react

It is interesting how the Redux intro mentions it casually in passing: http://redux.js.org/docs/introduction/PriorArt.html

The question is: do you really need Redux if you already use Rx? Maybe not. It's not hard to re-implement Redux in Rx. Some say it's a two-liner using Rx .scan() method. It may very well be! ... If you don't care too much about it and want to go with the reactive data flow all the way, you might want to explore something like Cycle instead, or even combine it with Redux. Let us know how it goes!

It could be the RxJS "unreasonable" complexity, and, unfortunately, CycleJS doesn't look too simple either. And perhaps, the problem is not so much the complexity itself, but the lack of sufficient motivation and justification for it. So from this perspective, it is important to motivate any additional complexity, where such posts are of great help.

It might be good to try to exploit the best parts of the streams first, such as FL and static land compliance, clarify the correspondence (natural transform) into arrays, which will help to make most methods derived from very few basic ones, then move to behaviours in the situations when there are clear advantages.

paldepind commented 7 years ago

I'm happy you got something out of the blog post 😸

Perhaps it will be worth publishing it on Medium and see how the broader community reacts.

I know that a lot of people publish on Medium. But, will doing that get more people to see it?

I think one problem with the adoption of FRP is that many reactive libraries have a lot of accidental complexity. Hot vs cold observables, laziness, updates that don't happen as one would expect. The key to avoiding this complexity is to specify simple semantics that can explain the behavior of the entire library. In this way, users can have a simple mental model that the library should obey without dragging users into implementation details.

paldepind commented 7 years ago

@JAForbes This is an answer to https://github.com/paldepind/functional-frontend-architecture/issues/23#issuecomment-299769722.

I think your post was very interesting. There are definitely things I don't agree with and things I don't understand. But, to make sure the conversion is moving forward and not in circles I think it would be beneficial to figure out what we do agree on :smile: So below is a

CPUs

I think we fully agree with regards to CPUs. What you're saying is that from the point of view of a programmer a CPU is discrete. It provides a discrete abstraction consisting of instructions, memory cells, etc. But you also write that if we dive deep enough we will find that:

[...] the universe is always continuous / analogue while abstractions can be discrete.

So at some level CPUs are continuous. Just not at the programmable level. That was what I meant when I said that CPUs are continuous at the physical level. As far as I can tell you are saying the same thing.

Implementation details should not affect our model

But, the above is pretty irrelevant :wink:. Because, we also agree that technical details of the CPU should not dictate how we write our code. Which I think is what you're saying here.

I think it's important to accept that computers internally operate on a discrete model, but that doesn't mean we have to build our abstractions in the same way.

I completely agree with that. CPUs are imperative and discrete but that is just details that should be abstracted away. Details about computers should not prevent us from writing the code that we want to write.

The universe is fundamentally continous

You write

I think the universe is always continuous / analogue while abstractions can be discrete.

Which I fully agree with. I think discrete models are always invented as concepts in our brain. You have a great example of that here:

Even the notion of a click being discrete is entirely abstract. In reality a mouse button receives an amount of continuous pressure, there is a threshold where we decide that pressure warrants a press. Then there is a continuous amount of time a user can hold the button down before releasing, and releasing in itself is a continuous model of force. Finally we encounter a minimum force threshold and we call this a click.

The mouse itself isn't discrete. But we as humans invent the concept of a discrete "click".


Now to what I don't understand: your definition of continuous.

I only know what continuous means on real-valued functions. Myy understanding is exemplified by this image:

The red function is continuous while the blue function is not. The explanation of continuous from Wikipedia that you quoted matches this. But, that definition is only valid for real-valued functions. And you also use continuous when talking about functions whose domain is not the reals.

Continuous is a mathematical property. And mathematical speaking two functions are equal if they for all input returns the same value. And if two functions are equal they can either both be continous or both be discontinous. That is why this code example doesn't make any sense to me:

// continuous boolean model (unaware of the specific value)
// modelled as a shared relationship for all values
b.map( x => !x )

// discrete boolean model (aware of each "event" as a discrete unit)
// modelled as discrete outputs for discrete inputs
s.map( x => x == true ? false : true )

The two functions passed to map are equal. It's the exact same function written in two different ways. In the second function, the definition of the negation operator has been inlined. Saying that one of them is continuous while the other is not is like saying that the number 3 + 7 has some property that 4 + 6 doesn't have. It's the same number so they must have the same properties.

This makes me think that maybe you're not using the word continuous in the mathematical sense. Maybe you're using it to talk about some related property.

But I still don't see any significant difference between the two models in the example. You write:

Notice the output is the same, but the model is very different. A discrete model is aware of each possibility, and handles them discretely. If we have a case statement for each discrete possibility, we are not continuous.

I would say that both ways of writing the functions are "aware of each possibility". When you write ! you are embedding knowledge that you are dealing with booleans. And you are also aware how ! deals with each boolean value. I can't see how inlining the definition of ! is making the code more aware of booleans.

dmitriz commented 7 years ago

Now to what I don't understand: your definition of continuous.

There is unfortunate mismatch of terminology in- and outside math. What people outside math call "continuous" means often what mathematicians call "continuum". The set of all values in real line without gaps is a continuum. A function on a continuum does not need to be continuous. This might be the confusion here.