We’re all taught that we should write loosely coupled code so that changes in one code module or function won’t affect another function or module. Like all things, it’s easier said than done.
One of the goals of Avian Computing is to make loosely coupled code easy to create. Each of the birds is a separate entity with its own set of variables. The only way it can get info to operate on is to eat from the TupleTree; the only way it can pass on info is to store food in the TupleTree.
While this might seem restrictive, it is conceptually simple and follows the ways of nature. But this simple mechanism structures our thinking so we automatically produce loosely coupled code. The only way that one bird can affect another bird is by making a change to the food it stores. The only way that a bird can be affected by a different bird is by changing its response to the food that it ate.
For example, if a function changed it’s return value from “Fred” to “10”, the code that receives the returned value may or may not know how to handle the new value. Usually we manage this changed return value by remembering all the places where we use that changed function and update it’s code. And then we usually forget one place where we used, causing the system to crash or otherwise go wonky, whereupon we do a code search of our project and find those other instances where we used it. And if we’re lucky, the changed function isn’t in a library that is used in other projects that would need to be changed.
In Avian Computing, because we share everything thru the TupleTree, we force changes up to the surface or force them down inside the bird. If changing “Fred” to “10” only affects one function in that one bird, the change can never get outside to affect other birds. If the change from “Fred” to “10” should affect other birds, the change is forced to the surface, there in the shared food, where everyone can see it. No pathological invisible couplings lingering inside the code.
Of course, there are times when we’ll have multiple instances of the same bird but we want to have different responses. Perhaps we want it to return “Fred” in some circumstances and return “10” in other circumstances. In this case, the ability of the StdBird to save two different kinds of food is used. If it should return “Fred”, the bird will save a “FredFood” to the TupleTree. If it should return “10”, the bird will save a “10Food” to the TupleTree.
The developer will have to create a FredFood type and a 10Food type, as well as a FredEater bird and a 10Eater bird, but this sounds harder than it really is. Unless there’s something really unique in the new food types, typically they are just sub-classes of StdFood with their own constructor that identifies their food type. Same for new birds. New birds are usually just sub-classes of either StdBird or some other bird, with changes to how they digest their food.
Sub-classing food and birds this way keeps the code for the different behaviors separate and ensure that there are no invisible connections in the behaviors when processing “Fred” or “10”. This loose coupling of modules also allows developers to substitute different versions of FredEater or 10Eater without affecting the overall program.
The ConcurrentExplorer (ConcX) makes bird substitution easy; when ConcX is running, you can configure up to 100 individual birds that will participate in your program. ConcX allows you to start the desired birds (or all birds) and then stop or start any of the birds without negatively affecting (crashing) the rest of the birds. So, for example, you might stop the FredEater bird and then start the FredAEater bird and then stop it and then start the FredBEater bird. Or run Fred, FredA, and FredB and let them compete and show you which one provides the best results.
This flexibility is only available in ConcX (and Avian Computing) because of its inherent loose coupling. All of the birds in the flock behave individually in just the way they were configured, without affecting the other birds, and together as a flock they operate in parallel to accomplish the goals of the program.