Related
According to Redux Style Guide, it is strongly recommended to connect more components to read data from the store.
For example, rather than just connecting a <UserList> component and reading the entire array of users, have <UserList> retrieve a list of all user IDs, render list items as <UserListItem userId={userId}>, and have <UserListItem> be connected and extract its own user entry from the store.
This, though, sounds a bit contradicting to what has been encouraged earlier in "Usage with React" section to separate presentational components from container components where the presentational components are to read data from props, not from the store.
Does this mean that:
It is best practice to keep the number of presentational components to minimum, hence increasing the number of stateful components?
Or the connected components can also be actually stateless components?
I'm a Redux maintainer, and I wrote the Style Guide page.
The short answer is that the Redux docs have been written over time, and thus some of the older docs page are out of date.
The Style Guide is our latest and current advice on how you should write your app.
We're in the process of rewriting the Redux core docs. That exact "Usage with React" page is something I intend to rewrite very soon, and when I do, I'll be dropping the terms "presentational" and "container" entirely.
I'd also encourage you to read my post Thoughts on React Hooks, Redux, and Separation of Concerns and watch my React Boston 2019 talk on Hooks, HOCs, and Tradeoffs to get some more thoughts on how hooks change the way we think about writing components.
Like everything in programming, there is a balance.
On the one hand, you have separation of concerns, making sure each block of code is focusing on one task. This can help reduce the complexity of a given component.
On the other hand, you have reduction of parameters, reducing the brittleness of your code by keeping track of fewer parameters at any given moment.
The first bullet is typically required when your state management is complex, or you have to manage server connections, and want to keep that work separate from the presentation to reduce confusion.
Redux takes care of that for you, by putting that code into the reducer. If you use the connect() higher-order component, that's exactly what you're doing: creating a component to translate state for your base presentation component. The useSelector() and useDispatch() hooks are another way of reducing the state management code in your component.
Redux stresses the second bullet because Redux's purpose is to reduce the clutter to the point that you don't need to separate your code into presentation and business logic components. Instead of passing several props back and forth, you can pass a single key, make a simple function inside your component to retrieve the data, and get on with the presentation directly.
The folks who wrote Redux also want to reassure folks that Redux is quite fast, and not to be afraid to use it generously.
My own experience is that Redux manages the business logic side of things well enough that I rarely need to create a separate wrapper component for business logic. The state code is a few lines calling hooks at the top, and that's it.
If I do have complex business logic, typically it involves deciding what state to display. That involves determining which key to use in my Redux state. So I might put all that logic into a wrapper, but the end result of the wrapper is a single key that my presentation component uses to pull the appropriate state from Redux.
I've recently seen this interesting presentation about reactive programming in Elm.
This made me wonder whether the language constructs used to implement the Mario game (in the shown presentation, and also in the picture below) can be mapped to similar high level language constructs either in pure JavaFX or in JavaFX combined with RxJava ?
In other words, would it be possible to express the Mario game implemented in Elm using the same conceptual abstractions (i.e. time dependent values) in either JavaFX alone or in JavaFX+RxJava ?
So if an experienced 1) JavaFX programmer or an experienced 2) JavaFX+RxJava+Functional programmer wants to port the Mario game from Elm to either 1) JavaFX or to 2) JavaFX+RxJava then which programmer could accomplish this task by using similar high-level abstractions as was used in Elm ?
The abstractions I have in mind for JavaFX are bindings and for JavaFX+RxJava are bindings+Observables/Subjects/Subscription.
I had a quick look at Elm and it is quite impressive (and expressive).
In Elm you construct your scene as
Signal Element
which in JavaFX would be roughly equivalent to
ObservableValue<Node>
Naive translation to JavaFX would mean that you swap the whole scene on each update, which is prohibitively costly. In Elm, Element is an immutable abstract representation of a scene node, constructed off the screen (off the DOM). It is cheap enough to recreate the whole scene graph on every update (because of immutability, it is safe to reuse unchanged branches). The root Element is rendered to DOM, but, as I understand it, only the first time the full DOM is constructed. On subsequent updates, Elm runtime runs an algorithm to compare the new root element to the old one and only modifies those parts of the DOM that need updating, which is fast enough. The same technique is used by React.js. So both Elm and React.js provide high-level functional abstraction, but use mutability under the covers for performance reasons.
With some additional syntactic noise, you can translate most Elm constructs to JavaFX (*). For example,
lift2 display Window.dimensions gameState
is equivalent to
import static org.fxmisc.EasyBind.*;
combine(Window.dimensions, gameState, display)
The missing piece is the library of immutable abstract widget representations and their efficient rendering into JavaFX scene graph.
What you could do is:
create such a widget library for JavaFX;
or even compile Elm to JavaFX (instead of HTML+JavaScript).
I would love to see either of those done.
(*) I think Elm records are very powerful and would require a lot of boilerplate in Java (or Scala).
Elm:
type Point = { x:Float, y:Float }
p = { 0.0, 0.0 }
q = { p | x <- 5.0 }
Java:
class Point {
public final float x;
public final float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public Point updateX(float x) {
return new Point(x, y);
}
public Point updateY(float y) {
return new Point(x, y);
}
}
Point p = new Point(0.0, 0.0);
Point q = p.updateX(5.0);
Scala:
case class Point(x: Float, y: Float)
val p = Point(0.0, 0.0)
val q = p.copy(x = 5.0f)
I say it is possible, without actually doing it :-)
This is only a guess as I have little experience in this field and none at all with Elm.
I think you will get closest to Elm by creating a ScalaFX wrapper for ReactFX, though you could probably also use Java 8 lambdas in combination with ReactFX rather than Scala. You will likely have to implement quite a few extensions to ReactFX as well as various other facilities to get the abstractions and elegance you desire.
Checkout Deprecating the Observer Pattern if you have not already done so. I think that it is quite relevant to this topic especially with regards to thinking about a reactive programming model as opposed to an observable model (which is what JavaFX ChangeListeners are). That said, I think if you use libraries such as ReactFX which are built upon the JavaFX property and listener frameworks, then you likely end up with the higher level abstraction of event streams that are useful for reactive programming.
Sure, you could make a Mario game in JavaFX without using ReactFX, but the code would be quite different and you would be working at a different level abstraction to that which you would be using with ReactFX. So pure JavaFX, without ReactFX would be less Elm like.
For a network layer, akka could be used to provide a reactive framework for multi-player games, this would allow you to use reactive programming top to bottom, both in your UI and in your communications system.
For the Sprite animations in the Mario sample, adopt the netopyr code for Creating a Sprite Animation with JavaFX.
For the physics calculations you could use the JBox2D library. You would need to create some kind of reactive wrapper around JBox2D to make it aware of stuff like JavaFX properties and ReactFX event streams and subscriptions. For the simplistic Mario sample, use of an external library would probably be more trouble than its worth. Instead you could use a basic home grown physics model (e.g. something like this ball animation experiment) and hook it into your reactive framework.
I advise getting in touch with Tomas Mikula, who has created ReactFX, and asking him your reactive programming for JavaFX questions. Robert Ladstätter has also done some cool stuff with ScalaFX and might be able to give you some further advice, though I don't know if he's worked with Reactive programming at all. I'd also suggest pinging the ScalaFX users forums, but I see you've already done that :-)
The Elm Mario sample is pretty simple, so I advise trying the translation to ReactFX yourself. Then I think the answer to your question will become self-evident. Now that I've written this => if you don't actually implement it and publish your solution as the correct answer, I might have to hunt you down and you wouldn't want that :-)
I'm currently comparing traditional object-oriented GUI programming with functional GUI programming for my master's thesis. In fact, one part that I am about to begin before long is using Elm to recreate a number of traditional GUI programs in JavaFX/ScalaFX. When I'm finished with that I will probably be able to give a much more precise answer but I still want to provide some generally useful pointers now.
First, there are two good papers that provide an overview of the FRP landscape, namely “A Survey on Reactive Programming“ and “Towards Reactive Programming for Object-oriented
Applications”. The former, in particular, provides a taxonomy of FRP systems that distinguishes “Siblings of FRP” and “Cousins of FRP” (alluding to their kinship to the ideas of Fran). Siblings of FRP focus on time-varying values, Cousins of FRP focus on event streams, observable collections and asynchrony and don't have primitive abstractions for the representation of time-varying values. With that distinction in mind, Elm is a Sibling of FRP and RxJava/ReactFX is a Cousin of FRP. I guess you could say they are different animals even though both are FRP systems and have overlaps, naturally.
Second, the paper “Crossing State Lines: Adapting Object-Oriented Frameworks to Functional Reactive Languages” provides a very nice and succinct summary of the differences between the FRP approach as in Elm and the traditional OOP approach as in JavaFX:
OO makes state explicit but encapsulates it, whereas state in FRP is hidden
from the programmer by the temporal abstractions of the language.
So there is a fundamentally different approach to program design. Therefore, to me it is not yet clear if and how you can achieve the “same” high-level abstractions with JavaFX(+RxJava/ReactFX) as with Elm. That does not mean it is not possible but it is not obvious on the first glance.
Finally, a general observation about Elm and its extreme “alienness” compared to the traditional OOP approach from my thesis to make the obstacles in achieving the same result in JavaFX clearer:
A lot of GUI libraries even in pure functional languages are essentially a
thin shim over an (imperative) object-oriented GUI toolkit. This means, for
example, that widgets are not pure functions. Widgets have state and therefore
an identity. If we metaphorically turn up the functional knob on a GUI toolkit's
control board to its maximal value we must arrive at a situation where even
the widgets are nothing but pure functions. Elm is such an example of a
maximally functional language and GUI toolkit at the same time. As callbacks
are all about side-effects Elm naturally does not use callbacks whatsoever for
behavior-related code but instead FRP. Where customization in a OOP language
is most often achieved with inheritance, in Elm there is only the way of
function composition...
The key point is that in Elm the views are pure functions and, basically, Elm forces you to structure the code this way. In JavaFX you aren't forced so you must deliberately take care to do it.
By the way, there is a game skeleton for Elm games like Mario. You can study it and think about how you would design your JavaFX program following this skeleton. Maybe you could even create such a skeleton for JavaFX after your studies? I'd be interested in it.
I've posed this question a year ago. Since then I have learned a thing or two about FRP. I think the answer is no, at least not easily with RX, it would be much better to use JavaFX+Sodium FRP library.
Why not (very easily) possible with RX ? Because ELM is FRP, while RX is FRP minus transactions (vaguely speaking). So practically, if someone would want to implement the Mario Game in ELM then she would have to add transactions to RX (to avoid glitches).
Every web development framework I've come across, including the best designed ones (Web2Py, Kohana) make use of some form of global variables to represent objects that are global within the application domain- for example, objects representing 'request' and 'response'.
On the other hand, the idea that global variables are bad is one of the fundamental axioms of programming. Indeed the now common disparagements of the singleton pattern usually point to the fact that it's nothing more than globals in disguise, as if that were explanation enough.
I'm trying to understand once and for all how globals can be so condemnable and at the same time be a seemingly indispensable part of all our web frameworks?
What is a global? Taking your text I assume you mean a variable that's declared at global scope. Such variable could be overridden by any assignment and break existing functionality.
However, in OO languages, everything is inside a class and assignment can be wrapped in gettors and settors for properties, or completely hidden behind methods. This gives a safe way of dealing with globals. Note that in proper OO languages (Java, C#, VB.NET etc) it is not possible to have global variables (sometimes a language construct suggests otherwise, but static fields in C# or modules in VB, mixins in Ruby are all wrapped in classes and thus not truly global).
A singleton, you mention it, is a special kind of global. As a designer you can control how many instances run of it. A car only needs one engine, a country only one government (or war breaks loose) and a program needs only one main thread. Globals are a necessity to programming, the real discussion should not be, do we need them, but how to solidly create and use them.
You say that request and response objects are globals in web development. They are not. They are (usually, depending on your toolset) convenience variables set in scope before your code is run. Since a web application can have multiple request objects at any given time, I think these are a poor example of a global variable. They are not (but they are usually local and a singleton to your current thread).
One important feature that you cannot cover in traditional procedural languages (like Basic, Pascal, C) is access control and thus concurrency and thread safety for global variables. In .NET for instance, any static method or property in the BCL (one could say that any static variable is global by definition) is thread-safe by design. Guidelines for user-defined static methods or properties suggest you do the same.
EDIT: the danger is with languages that allow global variables but at the same time propagate themselves as truly OO. While these are wonderful languages, it is indeed dangerous to step out of the protection of OO and create globals in for instance Perl, Python, Ruby, PHP.
I don't know exactly in what context those globals are used in web frameworks, but anything global starts to create problems as soon as you need to have solid access control. If you start to use such a global in concurrently executing program, it's quite hard to say who and when accessed and changed it. It creates so-called shared state. This makes debugging even more difficult.
Anyway, I am not really in favour of such statements. This only leads to oversimplifications. You have to weight you requirements and then decide if this or that pattern brings more positive or negative effects...
I am very new to c++ and confused between what is the difference between modular programming and function oriented programming.I have never done modular programming so I just know modules by definition that it contains functions.So what is the difference between a sequential(function-oriented language)and modular programming?Thanks in advance.
EDIT:
I was reading about C++'s OOP.It started something like what is unstructured programming, than gave a basic idea about structured programming, than modular programming and finally,OOP.
Modular programming is mostly a strategy to reduce coupling in a computer program, mostly by means of encapsulation.
Before modular programming, local coherence of the code was ensured by structured programming, but global coherence was lacking: if you decided that your spell-checking dictionary would be implemented as a red-black tree, then this implementation would be exposed to everyone else in the program, so that the programmer working on, say, text rendering, would be able to access the red-black tree nodes to do meaningful things with them.
Of course, this became hell once you needed to change the implementation of your dictionary, because then you would have to fix the code of other programmers as well.
Even worse, if the implementation detail involved global variables, then you had to be exceedingly careful of who changed them and in what order, or strange bugs would crop up.
Modular programming applied encapsulation to all of this, by separating the implementation (private to the module) from the interface (what the rest of the program can use). So, a dictionary module could expose an abstract type that would only be accessible through module functions such as findWord(word,dictionary). Someone working on the dictionary module would never need to peek outside that module to check if someone might be using an implementation detail.
They are both ways of structuring your code. If your interested in function-oriented programming and want to understand it a bit better, I'd take a look at lisp. C++ isn't truly function oriented as every function should return a value yet C++ functions can return void (making it a procedure rather than a function), so it's not a true functional programming language in the sense.
"I have never done modular programming so I just know modules by definition that it contains functions".
Modules are a level higher than functions.
That's a good start. Think of a function as a unit of work that does something and when you have several functions that you can group in a certain way, you put them in a module. So, string.h has a bunch of functions for working with strings, but you simply include the header and you have access to all those functions directly. You can then reuse those modules in other projects as you'd already used the modules previously and they've been (I assume) debugged and tested and stop people from reinventing the wheel. The whole point is to benefit from the cumulative experience.
I'd suggest you think of a project you'd like and write some functions and think about how you'd like to organize the code for another developer to use.
Hope this is of some use to you.
I believe functional programming leads us to micro services paradigm as for now while modular programming tends to similar to OOP concept.
I've recently been learning about functional programming (specifically Haskell, but I've gone through tutorials on Lisp and Erlang as well). While I found the concepts very enlightening, I still don't see the practical side of the "no side effects" concept. What are the practical advantages of it? I'm trying to think in the functional mindset, but there are some situations that just seem overly complex without the ability to save state in an easy way (I don't consider Haskell's monads 'easy').
Is it worth continuing to learn Haskell (or another purely functional language) in-depth? Is functional or stateless programming actually more productive than procedural? Is it likely that I will continue to use Haskell or another functional language later, or should I learn it only for the understanding?
I care less about performance than productivity. So I'm mainly asking if I will be more productive in a functional language than a procedural/object-oriented/whatever.
Read Functional Programming in a Nutshell.
There are lots of advantages to stateless programming, not least of which is dramatically multithreaded and concurrent code. To put it bluntly, mutable state is enemy of multithreaded code. If values are immutable by default, programmers don't need to worry about one thread mutating the value of shared state between two threads, so it eliminates a whole class of multithreading bugs related to race conditions. Since there are no race conditions, there's no reason to use locks either, so immutability eliminates another whole class of bugs related to deadlocks as well.
That's the big reason why functional programming matters, and probably the best one for jumping on the functional programming train. There are also lots of other benefits, including simplified debugging (i.e. functions are pure and do not mutate state in other parts of an application), more terse and expressive code, less boilerplate code compared to languages which are heavily dependent on design patterns, and the compiler can more aggressively optimize your code.
The more pieces of your program are stateless, the more ways there are to put pieces together without having anything break. The power of the stateless paradigm lies not in statelessness (or purity) per se, but the ability it gives you to write powerful, reusable functions and combine them.
You can find a good tutorial with lots of examples in John Hughes's paper Why Functional Programming Matters (PDF).
You will be gobs more productive, especially if you pick a functional language that also has algebraic data types and pattern matching (Caml, SML, Haskell).
Many of the other answers have focused on the performance (parallelism) side of functional programming, which I believe is very important. However, you did specifically ask about productivity, as in, can you program the same thing faster in a functional paradigm than in an imperative paradigm.
I actually find (from personal experience) that programming in F# matches the way I think better, and so it's easier. I think that's the biggest difference. I've programmed in both F# and C#, and there's a lot less "fighting the language" in F#, which I love. You don't have to think about the details in F#. Here's a few examples of what I've found I really enjoy.
For example, even though F# is statically typed (all types are resolved at compile time), the type inference figures out what types you have, so you don't have to say it. And if it can't figure it out, it automatically makes your function/class/whatever generic. So you never have to write any generic whatever, it's all automatic. I find that means I'm spending more time thinking about the problem and less how to implement it. In fact, whenever I come back to C#, I find I really miss this type inference, you never realise how distracting it is until you don't need to do it anymore.
Also in F#, instead of writing loops, you call functions. It's a subtle change, but significant, because you don't have to think about the loop construct anymore. For example, here's a piece of code which would go through and match something (I can't remember what, it's from a project Euler puzzle):
let matchingFactors =
factors
|> Seq.filter (fun x -> largestPalindrome % x = 0)
|> Seq.map (fun x -> (x, largestPalindrome / x))
I realise that doing a filter then a map (that's a conversion of each element) in C# would be quite simple, but you have to think at a lower level. Particularly, you'd have to write the loop itself, and have your own explicit if statement, and those kinds of things. Since learning F#, I've realised I've found it easier to code in the functional way, where if you want to filter, you write "filter", and if you want to map, you write "map", instead of implementing each of the details.
I also love the |> operator, which I think separates F# from ocaml, and possibly other functional languages. It's the pipe operator, it lets you "pipe" the output of one expression into the input of another expression. It makes the code follow how I think more. Like in the code snippet above, that's saying, "take the factors sequence, filter it, then map it." It's a very high level of thinking, which you don't get in an imperative programming language because you're so busy writing the loop and if statements. It's the one thing I miss the most whenever I go into another language.
So just in general, even though I can program in both C# and F#, I find it easier to use F# because you can think at a higher level. I would argue that because the smaller details are removed from functional programming (in F# at least), that I am more productive.
Edit: I saw in one of the comments that you asked for an example of "state" in a functional programming language. F# can be written imperatively, so here's a direct example of how you can have mutable state in F#:
let mutable x = 5
for i in 1..10 do
x <- x + i
Consider all the difficult bugs you've spent a long time debugging.
Now, how many of those bugs were due to "unintended interactions" between two separate components of a program? (Nearly all threading bugs have this form: races involving writing shared data, deadlocks, ... Additionally, it is common to find libraries that have some unexpected effect on global state, or read/write the registry/environment, etc.) I would posit that at least 1 in 3 'hard bugs' fall into this category.
Now if you switch to stateless/immutable/pure programming, all those bugs go away. You are presented with some new challenges instead (e.g. when you do want different modules to interact with the environment), but in a language like Haskell, those interactions get explicitly reified into the type system, which means you can just look at the type of a function and reason about the type of interactions it can have with the rest of the program.
That's the big win from 'immutability' IMO. In an ideal world, we'd all design terrific APIs and even when things were mutable, effects would be local and well-documented and 'unexpected' interactions would be kept to a minimum. In the real world, there are lots of APIs that interact with global state in myriad ways, and these are the source of the most pernicious bugs. Aspiring to statelessness is aspiring to be rid of unintended/implicit/behind-the-scenes interactions among components.
One advantage of stateless functions is that they permit precalculation or caching of the function's return values. Even some C compilers allow you to explicitly mark functions as stateless to improve their optimisability. As many others have noted, stateless functions are much easier to parallelise.
But efficiency is not the only concern. A pure function is easier to test and debug since anything that affects it is explicitly stated. And when programming in a functional language, one gets in the habit of making as few functions "dirty" (with I/O, etc.) as possible. Separating out the stateful stuff this way is a good way to design programs, even in not-so-functional languages.
Functional languages can take a while to "get", and it's difficult to explain to someone who hasn't gone through that process. But most people who persist long enough finally realise that the fuss is worth it, even if they don't end up using functional languages much.
Without state, it is very easy to automatically parallelize your code (as CPUs are made with more and more cores this is very important).
Stateless web applications are essential when you start having higher traffic.
There could be plenty of user data that you don't want to store on the client side for security reasons for example. In this case you need to store it server-side. You could use the web applications default session but if you have more than one instance of the application you will need to make sure that each user is always directed to the same instance.
Load balancers often have the ability to have 'sticky sessions' where the load balancer some how knows which server to send the users request to. This is not ideal though, for example it means every time you restart your web application, all connected users will lose their session.
A better approach is to store the session behind the web servers in some sort of data store, these days there are loads of great nosql products available for this (redis, mongo, elasticsearch, memcached). This way the web servers are stateless but you still have state server-side and the availability of this state can be managed by choosing the right datastore setup. These data stores usually have great redundancy so it should almost always be possible to make changes to your web application and even the data store without impacting the users.
My understanding is that FP also has a huge impact on testing. Not having a mutable state will often force you to supply more data to a function than you would have to for a class. There's tradeoffs, but think about how easy it would be to test a function that is "incrementNumberByN" rather than a "Counter" class.
Object
describe("counter", () => {
it("should increment the count by one when 'increment' invoked without
argument", () => {
const counter = new Counter(0)
counter.increment()
expect(counter.count).toBe(1)
})
it("should increment the count by n when 'increment' invoked with
argument", () => {
const counter = new Counter(0)
counter.increment(2)
expect(counter.count).toBe(2)
})
})
functional
describe("incrementNumberBy(startingNumber, increment)", () => {
it("should increment by 1 if n not supplied"){
expect(incrementNumberBy(0)).toBe(1)
}
it("should increment by 1 if n = 1 supplied"){
expect(countBy(0, 1)).toBe(1)
}
})
Since the function has no state and the data going in is more explicit, there are fewer things to focus on when you are trying to figure out why a test might be failing. On the tests for the counter we had to do
const counter = new Counter(0)
counter.increment()
expect(counter.count).toBe(1)
Both of the first two lines contribute to the value of counter.count. In a simple example like this 1 vs 2 lines of potentially problematic code isn't a big deal, but when you deal with a more complex object you might be adding a ton of complexity to your testing as well.
In contrast, when you write a project in a functional language, it nudges you towards keeping fancy algorithms dependent on the data flowing in and out of a particular function, rather than being dependent on the state of your system.
Another way of looking at it would be illustrating the mindset for testing a system in each paradigm.
For Functional Programming: Make sure function A works for given inputs, you make sure function B works with given inputs, make sure C works with given inputs.
For OOP: Make sure Object A's method works given an input argument of X after doing Y and Z to the state of the object. Make sure Object B's method works given an input argument of X after doing W and Y to the state of the object.
The advantages of stateless programming coincide with those goto-free programming, only more so.
Though many descriptions of functional programming emphasize the lack of mutation, the lack of mutation also goes hand in hand with the lack of unconditional control transfers, such as loops. In functional programming languages, recursion, in particularly tail recursion, replaces looping. Recursion eliminates both the unconditional control construct and the mutation of variables in the same stroke. The recursive call binds argument values to parameters, rather than assigning values.
To understand why this is advantageous, rather than turning to functional programming literature, we can consult the 1968 paper by Dijkstra, "Go To Statement Considered Harmful":
"The unbridled use of the go to statement has an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress."
Dijkstra's observations, however still apply to structured programs which avoid go to, because statements like while, if and whatnot are just window dressing on go to! Without using go to, we can still find it impossible to find the coordinates in which to describe the process progress. Dijkstra neglected to observe that bridled go to still has all the same issues.
What this means is that at any given point in the execution of the program, it is not clear how we got there. When we run into a bug, we have to use backwards reasoning: how did we end up in this state? How did we branch into this point of the code? Often it is hard to follow: the trail goes back a few steps and then runs cold due to a vastness of possibilities.
Functional programming gives us the absolute coordinates. We can rely on analytical tools like mathematical induction to understand how the program arrived into a certain situation.
For example, to convince ourselves that a recursive function is correct, we can just verify its base cases, and then understand and check its inductive hypothesis.
If the logic is written as a loop with mutating variables, we need a more complicated set of tools: breaking down the logic into steps with pre- and post-conditions, which we rewrite in terms mathematics that refers to the prior and current values of variables and such. Yes, if the program uses only certain control structures, avoiding go to, then the analysis is somewhat easier. The tools are tailored to the structures: we have a recipe for how we analyze the correctness of an if, while, and other structures.
However, by contrast, in a functional program there is no prior value of any variable to reason about; that whole class of problem has gone away.
Haskel and Prolog are good examples of languages which may be implemented as stateless programming languages. But unfortunately they are not so far. Both Prolog and Haskel have imperative implementations currently. See some SMT's, seem closer to stateless coding.
This is why you are having hard time seeing any benefits from these programing languages. Due to imperative implementations we have no performance and stability benefits. So the lack of stateless languages infrastructure is the main reason you feel no any stateless programming language due to its absence.
These are some benefits of pure stateless:
Task description is the program (compact code)
Stability due to absense of state-dependant bugs (the most of bugs)
Cachable results (a set of inputs always cause same set of outputs)
Distributable computations
Rebaseable to quantum computations
Thin code for multiple overlapping clauses
Allows differentiable programming optimizations
Consistently applying code changes (adding logic breaks nothing written)
Optimized combinatorics (no need to bruteforce enumerations)
Stateless coding is about concentrating on relations between data which then used for computing by deducing it. Basically this is the next level of programming abstraction. It is much closer to native language then any imperative programming languages because it allow describing relations instead of state change sequences.