Evolution of beans
and a comparison to other reactive frameworks.
The visual programming language I call Beans has been evolving quickly since it was begun in early May. As the project has progressed, I've learned a lot, but I have been continually assured in it's potential. I believe it has several key aspects that set it apart from existing counterparts, that I'll do my best to talk about that here.
Comparison with other frameworks
I say that the paradigm behind Beans is unlike anything I've seen before, I hope to explain this a bit more in detail with examples.
Object Oriented Programming
The one who keyed the term, Alan Kay has described the ethos of beans, and is in a big part of the inspiration for this project. But beans is not Object Oriented in the sense that most think of it, this is why I've chosen not to use that term.
Alan Kay quoted the 19th century philosopher Arthur Schopenhauer when describing what happened with his language smalltalk.
All truth passes through three stages. First, it is ridiculed. Second, it is violently opposed. Third, it is accepted as being self-evident. – Arthur Schopenhauer
Object Oriented means something completely different now, and I have no reason to tie this new paradigm to the boundless complexity and obfuscation of what OOP has become.
If you want to learn more about how I've been inspired, check out Alan Kay at OOPSLA in 1997 "The computer revolution hasn't happened yet"
I didn't mention OOP principles because I think it's not relevant, except for it's conception. Functional programming on the other hand has something to it.
Functional programming is different I think than other popular paradigms not because of it's abundance of capabilities, but for it's constraints. Pure functional code is marked by one thing, that there are no side effects. Avoiding side effects leads quickly to many of the other aspects of FP, but I believe are not really crucial to understanding it at a high level, so I won't mention these.
Avoiding side effects has many benefits, personally I find that using this style brings clarity of mind when working with complex systems. I will always tend towards functional programming principles.
But FP has one fatal flaw, its useless.
Functions take inputs and return outputs with no side effects, so if pure functional programming is always followed, the programs are only able to take the input, execute, then return the result.
Some command line tools could be programmed with pure functions maybe, but more applications of programs are interactive, or must respond to input at any time.
The only solutions I've heard are Monads, managed side effects, or just cave and mutate anyways.
No one actually understands Monads, and even if they did they would realize it doesn't actually enable interactivity out of the box. (Unless there was streaming logic under the hood, but we're getting ahead of ourselves)
Now we're getting closer, reactive programming is a huge inspiration for this project.
Wikipedia gives a vague description as always:
"reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change"
This paradigm is close, but not exactly what I'm getting at for beans. Many reactive frameworks are not standalone (with the exception of Elm), and are libraries or features for another language, such as the RX family.
While I haven't worked with such a framework, I have used reactive frontend frameworks such as vue and react. These frameworks were born out of the necessity to manage high interactivity and coordination that it takes to design a robust modern frontend application.
There is also a problem with these frameworks, they are either too domain specific in the case of reactive web frameworks, or they are not explicit enough, in the case of standalone reactive frameworks.
How to be explicit
From the Zen of Python "Explicit is better than implicit."
So being open and transparent is better than being closed and opaque. Yet most programming languages suffer from being way too implicit.
This is the reason global variables are bad. There's nothing inherently wrong with exposing something like a constant on a global level, but as soon as there's mutable global state, chaos ensues.
Why is this? For the same reason functional programmers prefer to avoid mutation altogether. If some state can be changed at any part during the program, then as the size of the program grows, it becomes harder to understand how that state will change over the lifetime of the program.
How can we avoid the pains of mutable state, while enabling the creation of useful programs?
When you come across a stream, you can understand where the water came from just by walking upstream. We take this for granted in the world we live in where proximity determines the effect.
But the same is not true in the world of programming. Even with pub/sub or observers, streams of data can come from anywhere there is a reference to the stream. It's not as simple as walking upstream when the streams could be coming from countless directions.
This is what beans strives to solve. Each bean lives somewhere, and it can pass messages to it's neighbors or receive messages or store a bit of data. That's it! If you're getting an unexpected result in a beans program, it is as easy as walking upstream, because the only way to get data is by receiving it from other beans in proximity.
What kind of effect would this have if understanding the flow of data was as easy as watching it flow like a stream?
Comparisons are fun but it's more productive to compare with where we came from! Beans has come a long way, and there's a long way to go. I'll share a bit about the challenges I've encountered along the way.
Working with uxn
Since before I started this project, it's been my desire to experiment with this paradigm in the uxn ecosystem. Uxn is very different from most languages out there, and at this time I'm still quite inexperienced working with it. Thankfully though, uxn is actually quite simple to understand, given its tiny implementation.
Additionally working with uxn can make seeming simple tasks more difficult. The given primitives are extremely basic, and it's up to the programmer to build up to the functionality as needed. I've found this has a great benefit, that is as your program is being built up, so are your intentions being changed. This means ideas are forced to be refined to the true intentions, removing all slag in the process, which would otherwise wax on as technical debt.
If you're feeling cornered and burnt out by your tech stack, I'd highly recommend checking out uxn, this tutorial is a great place to start.
Recursive bean definitions
This wasn't something I planned on adding at the beginning, but it became evident that it was necessary. Primitive beans are quite small in this implementation, as I've put a constraint that they can only store a single byte of memory, plus there's a finite number of slots given, so I'd like them to be absolutely necessary to deserve their place.
This means complex logic takes up space on the program, and this can be a problem given finite space, and potential changes to the program logic causing expansion. To solve this, I've added a new feature, that is recursive bean types.
Not recursive in the sense of recursive functions, but rather beans which can embed other beans within it. The inner bean being a reusable object which contains it's own beans, so storing it's own memory for each instance.
Think inception, where you can have a dream within a dream. Similarly, currently there is a rule that execution is paused while inner beans are executing, so time is "faster" the further down you go with respect to the highest layer.
This was pretty difficult to implement, and it's still a bit inefficient. Though I have plans and many ideas to integrate these inner beans to be a first class citizen.
More to come
Here's a couple more videos showcasing some of the recent work done with beans.
The top bean is the main one, holding only a few important cells. First in the storage cell which caches the last keyboard input.
Then it moves to the first inner cell, which generates a movement signal based on the key input. Essentially, if a go left, d go right, w go up, s go down, otherwise pause.
The resulting signal either goes left or right for x or y, and is either +1 or -1.
This signal gets routed to the current position counter which is altered by the result and then sends both x and y as the new coordinate.
These signals get split, one going to draw the new snake head. The other goes to the tail queue bean which stores the new head and propagates the old heads backwards. The last tail coordinate is pushed out and sent to the clear port to clear the last tail. (this queue is pretty inefficient, but does the trick, more capabilities will be available for efficiency soon)