Software Development

Correcting The Future

Decoupling, Abstraction and Atoms

For someone who likes low level programming, I'm reading about a lot of high level terms lately. Anyone who does things at a low level is usually more concerned about concrete ideas and the physical properties of what he or she is working on than the terminology used to describe them. Sure, you may need a blueprint, but the implementation itself usually doesn't require knowledge of how to create a blueprint.

Decoupling. What's it about? Basically, it's interdependance between systems. In simple terms, if you have class A that uses class B then that's coupling. Class A can't work without class B. If you change how class B works, you also have to change class A unless there is some standard protocol. And now we get into the area of decoupling. If there is a common interface between the two, then you could theoretically change class B and still have it work with class A as long as the interface is still there. So the coupling is now on the interface and not class B itself.

There is another more serious kind of coupling and that is when a class uses another object out of its own accord. Ie. via a singleton or some global mechanism. There is no way to accurately determine what objects are coupled with what other objects. There is no interface to use in this case. A redesign must be done (which may include interfaces).

Decoupling is related to abstraction in that both of these terms usually deal with objects although it can be used for functions as well. So abstraction is simply a way to organise your data and code in a more 'usable' format. The 'object' in Object Oriented languages is that abstraction. Instead of using primitive types and functions, now most everything is an object and has a different way to do things. In an optimal world, each object should be independant from others (decoupled). But in practice, an object that isn't reliant on others isn't very useful.

Ok, oversimplified discussions aside, what does all this have to do with anything? My suggestion is that code and data need to be decoupled. Also, objects and primitive types should be decoupled. Primitive types are atoms. Atoms hold a special place in programming languages and computing because the computer can only operate on these primitive types. Now objects should still be able to hold state information. So what exactly do I mean by decoupling code and data?

First, we have to accept the fact that data on its own does have its uses. They do not always have to be placed in an object with methods. An object that only contains data should be acceptable. Then there should be other objects that operate on this data. So we need two categories of objects. One for data and one for computations. In this way, we can pass data along to different objects for processing. If you've used Linux or Unix in general, then you're familiar with command pipes. After one program is done processing the data, it passes it on to the next in line. Why this obviously useful methodology has all but been adandoned is beyond me. It has proved itself for over 20 years. And realistically, in the world of the Web, many of the more common server side code is passing session information from one request to the other. But it is not seen in that fashion for the most part.

Actually, if you look around in specific niches, the idea of pipelining is coming back in full swing. It lends well to the idea of multiprocessing if there is a lot of data to process. Each program can be run at the same time as long as they stream the data instead of processing all data at once before passing the results along.

There is a very good reason why this works so well. It is simply an extension of how hardware processors work. Instructions basically read data in, process it and then passes that along to the next instuction. CPU manufacturers have taken advantage of this pipelining in their hardware architechture to provide much speed improvements.

Notice in all cases, the decoupling of code and data? While each 'program' does have state information as it processes the data, the data itself is seperate from the software. How many times do we pass along the entire object instead of decoupling the data? The idea was that the object was supposed to protect against corruption and mishandling the data. I think this is a backwards view. If the object is the only thing that can 'handle' the data, then only the object can provide functionality to manipulate it. So we subclass the object and add our extra functionality. But what if that functionality can be used elsewhere? See the problem with coupling data and code? I see this all the time and is one of the major problems with the OO paradigm IMNSHO.

Take a list for example. The list data structure should be seperate from what acts on it. A real world example is a user class as in my backgammon server. Everything revolves around this class. In fact, I can't think of anything the server does that doesn't act or require a user. Because everything needs this class, making its members private seems like a useless overhead. If C++ had provided properties, I could have specifed certain properties as read-only and provided direct access to the socket mechanism to the user's input and output buffers and socket handles for example. But there are no properties in the C++ standard, so I ended up doing the worst of all possible options in an OO world. I made all members public. GASP! Other classes were better handled such as the game rules class and RNG classes where these can be mostly self-contained. But there were no appropriate constructs for what I wanted to do overall. Writing get and set methods is unacceptable. I'm not sure who thought of this, but it's just awful.

So what should have I done? I've given a lot of thought to this and I don't know that there is a solution. Even properties would not have been enough. The socket handling mechanism needs access to the input and output buffers of each user as well as updating its state which can then lead to processing user message (again pipelining). Do I make the socket mechanism a friend? What about the message processing facilities? EVERYTHING updates a user. There seems to be no consistent and clean way to handle what is essentially coupling. No, what would have solved all my problems would have been to seperate all data from the user class. The user class would provide basic initialisation and functionality. But the data part of the user could be passed along to whatever part of the system needed to update it. The only thing missing is a standard way of doing these updates. Sometimes I think the worry of corruption or mishandling the data causes more trouble than it's worth. Primitive types don't have the same restrictions as objects, so are we intentionally giving ourselves more trouble than necessary?

Perhaps having a validation mechanism in place for raw data would be useful. I don't know. What I do know is that coupling is a very serious issue that the OO paradigm fails to resolve. I'll even go further and say that it makes it worse.

In fact, I'll go further again and say that most software process data in this pipelining fashion. Video players are an extreme form of this where the video data is passed through many codecs before finally being displayed. This could not be accomplished if objects were used. Web browsers evidently are pipelined just by the nature of sockets and streams. Word processors are pipelined when doing searches or any kind of formatting. I'm hard pressed to find something that is NOT pipelined. If you think about it, in overall programming practices, it would seem logical to pass along this data and have each 'station' process it before passing it along. But is that what we do in OO? No, we extend the class. To me, that's backwards.

I think this is one of the main reasons why OO is giving so many people problems. It doesn't adequately represent what they are trying to do.

Also, objects that contain both data and code is contrary to any real-world system. For example, sure, a car has tons of parts. But these parts pass fluids, electricity, air or force from one part to another. This is how things get done. When fuel goes into the valves of an engine before ignition, this fuel is not protected by a capsule. And the spark plug does not ask this capsule to please take some of this 'spark' and light the fuel for me because I obviously have no clue what I am doing. No, it is passed along in raw format. The sparkplug knows what to do.

This is what I don't like in the current way operator overloading works. For primitive types, at the most basic level, it's the CPU instructions that act on the primitive types. But if you try to extend this with more complex types, it's the types themselves that handle the operators. If you look at it, the operator function acts on data in both objects of the arguments. This is ok in this case because both arguments are the same type. In real situations though, we often pass data along to incompatible classes. So instead of passing along the data, we pass along the object and this results in unecessary coupling.

While objects do work great for certain situations, I think they bring their own set of problems. I propose to decouple data from classes when the need arrives. If you look at objects that handle XML and SQL, you can see what I'm talking about. While this encapsulation is in accord with the OO paradigm, it sure is a lot of hassle. I think all this relates to much of what I mentioned in the last blog entry about requesting "resources" and being able to act on them. Only in this case, you pass it along to the next "process" in the chain as well.

Are these common problems? This coupling of data is my main problem when I use OO. How pervasive is this problem? And should the pipeline methodology make a comeback? I think we have to take a step back and really look at what's happening when we code. What are the real objectives and do they really fit the OO paradigm? Is bare data too much of a risk? Unix has been using it for years. Was I wrong in making my user class public? All that's in it is data. I find it weird that I ended up using the pipeline methodology even though I had to jump through hoops to do it. Interestingly, the system works great because of this decision to make the user class public. Not in spite of it. But it should have been easier overall and I don't like using things that seem to contradict the accepted way of doing things (as far as the spirit of the language goes). What do you say about OO and coupling? And although C++ streams haven't been discussed, I think they fell out of favour a long time ago. Is it related to this coupling issue? I think so. Also, I admitted to something that in the programming world would be looked at very negatively by making my user class public. Have others had to create ugly solutions? In my view, it isn't ugly. It's just incompatible with OO. But it's perfectly acceptable and has been used for ages. And it works great.

DualityWell, This Sucks

Comments

avdi Saturday, January 14, 2006 7:12:38 PM

One of the biggest problems with C++ is not the language itself, but the culture that sprung up around it. Case in point is the dogmatism with which a lot of early C++ teachers pushed the "everything must be an object" rule. The irony here is that Bjarne Stroustrup himself never said this. In fact, if you read hiss writings, he repeatedly stresses that C++ is intended to be a multiparadigm language, and that you should only use a class when it makes sense to use a class.

This LtU conversation that I was just reading seems apropos to your ruminations on streaming. C++, and imperative languages in general, have traditionally made stream-based programming awkward. The functional crowd has always been a step ahead in that department, what with coroutines and generators and whatnot. Of course, you can do all that in C++, but it takes a level of intimacy with C++, STL, and generic programming that few coders have.

The problem you bring up, that of the user list which is accessed and updated by numerous components, sounds like it might profit from an application of the Visitor pattern. However it would be hard to say for certain without more detailed information.

avdi Sunday, January 15, 2006 1:25:58 AM

P.S. I ran across this tonight, and thought you might find it interesting: http://c2.com/cgi/wiki?FlowBasedProgramming

This is all quite apropos for me, since I've been reading through Hoare's original Communicating Sequential Processes paper.

Vorlath Sunday, January 15, 2006 5:03:57 AM

Funny you mention the LtU discussion. That was what in part spawned this blog entry. I want to thank you for the last link you provided. I've only skimmed it so far (and will have a more in-depth look at it later), but there are some good points near the middle of the page.

Hope you don't mind my quoting you:
"C++, and imperative languages in general, have traditionally made stream-based programming awkward."

I just think about sockets overall (and even XML, or both together) and this statement takes on a much more profound meaning.

Also, I know there are tons (thousands) of people reading my blog going by the stats. Please drop a note.

Unregistered user Wednesday, January 18, 2006 6:16:52 PM

Yoni writes: Data can certainly be decoupled from objects that handle it in an OO way but not in the way you describe your use of the 'User' object. I suppose you would agree that by exposing the internal representation of the User class you have introduced a lot of coupling to the system. As a consequence, the system may evolve to a point where changes to the representation of a User become impossible to perform. I do not see why you think the fact that you 'can't think of anything the server does that doesn't act or require a user' has anything to do with the system having to refer to the User class at more than a few places. If the server has to send the user's score to all clients whenever they ask for it than the User class should probably implement some sort of socket listener (or something else) and send the required information whenever it is necessary, there is no need to involve the rest of the system. I completely agree with you that having the data "guarded" by the object merely for fear of tampering is useless. What sould be closely guarded by an object is the "knowledge" (some say "implementation") encapsulated within the class that created it. The term Abstraction actually refers to the hiding of implementation details and not the hiding of data. A good example of decoupling an object from it's data is the GoF Flyweight Pattern. An XML stream, for example, can be used by one object to represent a User and at the same time used by another object to represent a searchable document. It is perfectly good OO practice to extract and manipulate the same stream by different classes - and these classes are completely decoupled. Decoupling does not meen an object should not rely on other objects (which as you mentioned isn't very useful), it meens an object should not rely on other objects to function at a certain way.

Vorlath Wednesday, January 18, 2006 9:55:25 PM

A few points: I agree that exposing the user class does introduce coupling on it's representation, but that's the point. It's like saying an XML parser is coupled to the XML specs. Well, that's the objective. What I disagree with is the coupling with an object containing that representation and asking IT to do all the work. To me, that kind of encapsulation doesn't lend very well to streaming operations. I see you point though. Am I trading one kind of coupling for another? Probably, but the second option works way better.

About the user class being used by the entire system: The are very few parts that do not require a user. Some parts are indeed independant, but are used either directly or indirectly by actions from the user. The socket mechanism cannot be placed directly with the user class because there are many parts that the socket mechanism deals with. It deals with the database, the timer events, the user connections to the OS (which is seperate from the user itself), listening sockets, closing sockets, not to mention the decryption and decoding and parsing of incoming and outgoing messages. It's better to have a standard way for the socket mechanism to do things instead of leaving it to each part. Remember that this is a massive multi-user system. An individual user is not allowed to hog the network resources. This is up to the socket mechanism to make sure each user gets its fair share of network access and is why all socket operations must go through it. Otherwise, you get severe lag and even disconnections which is very bad for online play. That is why I opted for the streaming option. When socket events come in, it chooses which one to deal with and sends that user through the system. Read/write buffers, decode&parse messages, process X messages, next user. I tried the other way. To this day, I have no idea how that would even be feasable.

I'm suprised that I'm not the only that thinks this: "I completely agree with you that having the data "guarded" by the object merely for fear of tampering is useless." The term abstraction also means to represent something by alternate means. This is the Object in your OO paradigm although it isn't mentioned much. I will agree that this is mostly about the implementations as you've said.

Your last paragraph, I think we're actually talking about the same thing. Relying on other objects is indeed coupling (the reason for interfaces). Logically, that coupling would be on it's functionality. So we're talking about the same thing I think.

I think overall, it's about streaming data. OO really does not make this easy. Implementing functionality in the object just does not work, especially if the data can be morphed completely. I think this is the only point of contention in your comment if I'm not mistaken, but you do bring valid points. I'm starting to get really interested in this idea of streaming.

Unregistered user Thursday, January 19, 2006 12:14:52 AM

Yoni writes: I hope my complete lack of knowledge of data streaming issues doesn't make me regret writing this... If I understand your comment correctly, you have a socket mechanism which deals with a lot of the complexity inherent in streaming data. That's fine, but why isn't it possible to allow objects outside of this mechanism (and decoupled) decide what to make of the data acquired after it was decoded and validated? Why must the socket mechanism "send that user through the system"? I would suggest designing the socket mechanism in a way that allowes it's "clients" (user objects for example) to register themselves as listeners for messages and perhaps provide some sort of feedback which will enable the mechanism to make the right decisions regarding the dispatch of messages. After all, while the streaming issue is very important to this application, it remains a low-level mechanism and should be easily replacable. And besides, it seems complex enough to be extremely valuable as a reusable mechanism and too complex to be burdened with application domain problems. I agree XML parsers are coupled to the XML specs - that's exactly why it is impossible to make drastic changes in the XML specs. In my opinion this is unacceptable in application programming where drastic changes are frequent. I'm not sure we're in sync about the "objects relying on other objects" issue. Coupling to me is the notion of an object relying on another object to function at a predictable way. Decoupling is an object relying on another object to simply do the work it was asked to do properly. When it comes to data I've come to agree with the rule that states the maintainability of a software system is inversely dependent on the amount of data that flows through the system. Whenever a method caller and the method's implementor have to agree upon the meaning of the data which is passed from one to the other - a point of failure and double maintanance is created and the maintainability of the code suffers.

Vorlath Thursday, January 19, 2006 2:09:16 AM

I like your last paragraph in particular. I won't comment on coupling as perhaps I simply view it in a different manner.

About the "user" class, making it register itself is implicit. Sometimes, it is explicit such as when it wants something to happen at a later time. But the message handling mechanism does this, not the user object. Otherwise, the user object would be coupled with the timer system. The user object could not exist on its own and introduces specialised functionality that is external to the user class itself.

And the timer system can handle a multitude of objects. In fact, it doesn't even care what you register in it (certain DB objects are passed through this also for timed updates and logs for example). It just passes it along to the socket system when the time comes.

Think of it as a mailing or delivery system. I cannot even fathom how you would represent a mailing system in a contemporary OO fashion. Asking the envelope to divulge its address every time would slow things down considerably. I don't think it's correct for the envelope to register itself. I think it's implicit when it is introduced into the system by an external entity (ie. you dropping it in the mailbox). That way, a whole host of functionality can be added without changing the envelope at all. If a package exceeds a certain weight, it can be automatically redirected to alternate shipping methods without the need for the object to request this or register itself or check . The system knows what to do. Otherwise, you would ask the envelope to handle the error or this change in processing. This is too much to ask of the envelope I think. I don't even think the envelope or package should be in charge of changing its contents. Just as my user class, I don't know what functionality I would include directly other than perhaps to initialise itself upon creation.

The point is not even if it is possible to write a mailing system in an OO fashion. The real question is if this is really the best way to implement it.

Unregistered user Thursday, January 19, 2006 10:38:49 AM

Yoni writes: To answer the question of whether OO is the right way to implement a mailing system, you must first have a complete understanding of what an OO solution to this problem would look like. "This is too much to ask of the envelope I think" - try to articulate the logic which leads you to this way of thinking. If you find yourself thinking this way makes the code more modular, readable or simply logical, you may be indivertibly reverting to procedural programming. This is very hard to avoid, I'm fighting it on a daily basis. "If a package exceeds a certain weight, it can be automatically redirected to alternate shipping methods without the need for the object to request this or register itself or check" - notice that in order to implement this shipping logic you are required to make changes in several locations. The envelope should now expose another property (or member variable) - weight. The shipping mechanism should access this property and act accordingly. The scale in the post office should access this property in order to set the weight of the package. So you have at least three locations in the code which have to agree on what the weight property actually represents (including the unit system used and validity constraints). This complexity increases drastically when additional properties are required for deciding which shipping method to use, it will soon become impossibly to support a new kind of package which should always be shipped in a certain way with no regard to its weight (maybe this kind of package doesn't even need to use the scale which may save time and money). "Asking the envelope to divulge its address every time would slow things down considerably" - this is exactly why you should never ask an envelope to divulge its address. Passing objects around is a lot faster than passing data (in C++ this would be done with pointers of course). The envelope should expose its address to the outside world only when it is absolutely necessary (ie sending it to the printer if you want to print it). The most important thing is that the outside world does not depend on the fact that an envelope contains an address, other objects may require that a valid address be passed to them in order to function but should never explicitly ask the envelope for it. "That way, a whole host of functionality can be added without changing the envelope at all." - you can easily add functionality without changing the envelope's data structure by simply adding it to the envelope class. If you are afraid of the God Class anti-pattern there are plenty of ways to break a class into decoupled pieces without exposing implementation details. My classes never pass the 300 code-line mark and are usually much smaller, yet I never find the need to use properties, Get/Set methods or even return values.

Dan Pologeadpologea Thursday, May 6, 2010 5:24:38 PM

I do agree with all the problems exposed here by you but I do not agree with all the reasons behind them in FBP environment. I agree if it is about OOP.
I've heard quite a lot about separation of code and data but the reasons behind it are sometimes very subtle or even wrong.
You remember the problems when the data registered on a tape, after several years, became useless just because the program (the code) that interpret them and gave them a meaning, was lost? In fact the program that knew what the data was and how to handle it, is a whole system, computer hardware + software. Obviously it is a strong link between them and they deserve to stay together but it does not mean that we should not have the freedom to extract, to use, reuse or to pass along the data alone (or subcomponents). It is cumbersome to pass along a parameter (IP in FBP) overloaded with its code or with other data members that are not needed to the receptor, just because that data needs some sort of “protection”. Anyhow, in an FBP system, the parameter (IP) is not a part of the emitter object structure but it is originated by it. It is a copy.

In OOP the problem comes from the fact a method is not a subcomponent like it is the case in FBP, because it breaks the rule of dependency, i.e. it depends on the whole its parent. In FBP a subcomponent could be reused elsewhere, in OOP a method could not.
In FBP there is not a major problem to aggregate code and data in order to reuse that code and that data elsewhere. In OOP it is a problem.
There is no strong reason to separate data and code as long as I can reuse the data and the code alone. But when we are saying reuse we have to be very careful because we talk about instances versus models. What we reuse, usually, it is the model not the instance. This is another discussion. The reason why the code is separated from the data is because the code is reused and not modified at runtime but the data usually it is modified at runtime.
I can find a system where the data has a similar weight to the model (class) as code has, i.e. the data is part of the model.
After all the code itself is data for the microprocessor. The bottom line is: separation of code and data does not mean to keep the code in one place and data into another, they can be merged together without the obligation to create strong dependencies as it happens in OOP. Data could be part of the model as the code is.

Originally posted by Vorlath:

Also, objects that contain both data and code is contrary to any real-world system.


I think it is exactly opposite. Any real-world system is a perfect mixture of data and code. Any real object obeys to the physical laws that act outside and inside it. The physical laws represent the “functions” that modifies the matter (i.e. data in your case). The functionality built by man or by nature and implemented in some mechanism (machinery) of a real object is the result of physical interactions between material components. Obviously the energy is involved, the energy pushes the matter through the restrictions given by the physical laws.
An object / component IS both data + code (matter/energy + physical laws). Functionality of a real object is built with matter, energy and physical laws. The software is a simulation of a real or potential world. In the real world the matter (data) is not separated by its functionality (physical laws). They act together all the time. Even if you extract a subcomponent from a machinery than it has its own functionality. It could be just a piece of matter and you can say that this corresponds in software to the pure data. In software you can simulate pure data with pure functions that generates it, if you want. Sometimes it's very useful, it's a kind of compression of the data into the laws that generate it.
A component in FBP could be pure data, pure functional or a mixture of these and it could resemble the real-world system. After all, it is exactly our desire, to resemble with the real-world system. Using the “classical” languages this is not happening, the programming is so hard to understand by non-programmers and even by programmers because it is not “natural”.

I asked myself why at first stage of a software program design, when the programmer draws diagrams to the client, those diagrams are not the first working compilable version of the program? Why there is a need for a hard translation into a software program? Why we don't compile the diagram? Because the “classical” language do not have a graphical representation, the data flow is hidden, because they do not have a good analogy in the real world. If the diagram has a good analogy with the real world and sure it does, that's why we are drawing it in the first place, than it means the diagram is a good representation of the real world we want to simulate or represent trough software. If there is a significant difference between the diagram and our software program it means in the end, the software program has not a good analogy with the real world. Obviously this is wrong and not desirable.

Originally posted by Vorlath:

For example, sure, a car has tons of parts. But these parts pass fluids, electricity, air or force from one part to another. This is how things get done. When fuel goes into the valves of an engine before ignition, this fuel is not protected by a capsule. And the spark plug does not ask this capsule to please take some of this 'spark' and light the fuel for me because I obviously have no clue what I am doing. No, it is passed along in raw format. The sparkplug knows what to do.


Your example is good but it does not mean there is no case where passing along an entire object is useful. The reason why usually we don't have to do this is because it is not necessary. Fully agree with you on this point but this happens “usually”.
There could be a lot of reasons why a fully functional component could be passed along as an IP (Information Packet in FBP). Your computer is a component that has been delivered from the shop to you. You can pass it along to me, in order to use it. It is a TOOL. The tools are functional components that have the property to move from one place to another in order to do their specific job. If you have a VCR you can pass it along to me with a tape to see it. You can have a specialized hammer or a saw. They are TOOLS, objects / components with functionality integrated that travel from one place to another.
You might say that in software we can do this in another way: create the tool in place (instantiate the tool's class), we don't need to transport an existing instance. This can be discussed because it depends on the “locality” but the tool (component) could bring some specific “parameters” delivered along with it.
I think that passing fully functional components (data + code) could be very useful in many situations, it should not be avoided just because OOP has a lot of problems with it. I disagree with so many aspects of OOP.

The data and the functions are ALWAYS separated at the lowest level. The atomic components are pure data or pure functions but not both. At higher level, the component (aggregate) is a mixture of both (functional or data subcomponents).

Originally posted by Vorlath:

An object that only contains data should be acceptable. Then there should be other objects that operate on this data. So we need two categories of objects. One for data and one for computations. In this way, we can pass data along to different objects for processing.


Agree on the fact we can pass along different object for processing but it doesn't mean this is absolutely necessary and also I do not agree that this creates dependency in FBP but it creates dependency in OOP. This will be highlighted later in my comment. So I will not split the world into two categories of objects (one for data and one for computations). Looking from outside of an FBP component, I don't know how pure functional or pure data a component could be. I just see input ports and output ports.

Originally posted by Vorlath:

Notice in all cases, the decoupling of code and data? While each 'program' does have state information as it processes the data, the data itself is separate from the software. How many times do we pass along the entire object instead of decoupling the data? The idea was that the object was supposed to protect against corruption and mishandling the data. I think this is a backwards view. If the object is the only thing that can 'handle' the data, then only the object can provide functionality to manipulate it. So we subclass the object and add our extra functionality. But what if that functionality can be used elsewhere?


Exactly, what if that functionality can be used elsewhere? I strongly believe that the object should not be the only one that can provide functionality in order to manipulate it. This is a ruinousness idea.

In FBP, using the functionality (subcomponent's model) elsewhere should not be a problem. Why is it a problem in OOP? Because a method (function in an object) is coupled with the entire object through the implicit parameter this. If you try to “cut” out this functionality (method) and reuse it elsewhere you can not just because it has a strong dependency: “this” (entire object).
A method in OOP is a subcomponent in FBP but between them is a strong difference: the method in OOP has knowledge about the entire parent and the subcomponent in FBP knows only its content and its parameters passed through its ports.
That's why each time you have a functional component embedded into an FBP component you can reuse it but in OOP you can't. Huge difference, right?

There is no strong reason in FBP not to pass along fully functional components if this is a real need.
Aggregating things in an OOP object creates coupling between them, aggregating things (subcomponents) in FBP does not create coupling between them. This is quite a difference. Where this difference comes from? First of all, a component membrane acts as an interface in FBP. This is exactly what a membrane is for in the real life (e.g. cells). Both components (emitter and receptor) depend on the data exchanged (IPs). If the data is simpler than the dependency is lower but the emitter does not depend on the receptor definition or vice-versa. If we look carefully, we realize that even the parent component does not depend on the IPs generated or received. A subcomponent from it that generates it or a subcomponent from the receptor that consumes it, depend on the IP definition.
Why this is not true in OOP? Because the object's method depends on the parameters but also on entire object (though this). An object's class will have a dependency on a lot of things. This is not the case with an FBP component (parent). The parent component just assembles the subcomponents and manage (if it is the case) the transfer of data from its input ports to the input ports of the corresponding subcomponents. The creation of a component in FBP could be very different comparing to the one in OOP. There are similarities with IoC (Inversion of Control / Dependency Injection). The subcomponents are injected into the parent component and linked together accordingly with the connections definition. It has similarities with the Actor Systems. Of course, the construction process could be “burned” at compile time but it will be less modular at runtime.
The default parameter this was a simplification like it was the stack to send the function's parameters. Unfortunately this creates dependency of a subcomponent (i.e. the method) on its entire parent.

Modelization in OOP is a mess. By the way, I have asked many programmers: what are the main criteria for gathering data and functions into the same object / class (in OOP). I have received various answers but none of them was satisfactory. The most dangerous one was: “I need the method to process the data inside the object and they are analog to a real object”. The latter was just a leap of faith.

In C++ we have different notions for what in FBP is the same thing: component. For instance, in C++ we have functions, classes, data variables. All of these have different syntax. A function is not declared like a class and a class is not declared like a function. A function resemble much better with an FBP component than a class, but the function has no state. It could have a state but it is per model (function) not per instance (function call). In order to have some similarity it should have been possible to instantiate functions like any class. A function instantiation is in fact an instantiation of a functional component. The equivalence of an FBP instance of a functional component model in OOP is a function call.

Vorlath Thursday, May 6, 2010 7:23:56 PM

Also, objects that contain both data and code is contrary to any real-world system.

I think it is exactly opposite.



In the real world, people and objects tend to manipulate other objects. I don't see them being coupled together all the time. A painter isn't part paint. He or she uses paint. Any manual labour is the same way with plumbers, electricians, framers, roofers, mechanics, etc. Even office jobs like lawyers, judges, accountants, etc all manipulate something that isn't coupled to them.

Fully agree with you on this point but this happens “usually”.



Good. I like different points of view.

There could be a lot of reasons why a fully functional component could be passed along as an IP (Information Packet in FBP).



True.

I understand some of your points after that, but this...

If you have a VCR you can pass it along to me with a tape to see it.



WTF?

A VCR (and DVD players) has (have) a slot for manipulating another object. The separation is clear.

Your examples aren't very convincing. I certainly agree that there could be times when you would bundle data+functionality. I'm hard pressed to think of such cases. In fact, if you ask people who create databases (the database software itself), they will tell you that the data is king. The software can come and go, but the data is forever. It's the same thing I've said before. You can manipulate the same data in many different ways if the tool can recognise said data. You can argue that the code is a form of metadata. That's certainly one angle. But having it stored along with the data isn't bad either.

it should not be avoided just because OOP has a lot of problems with it.



Agree 100%. I actually think it serves a purpose in OOP in many cases because of the way it's designed. A RNG certainly stores internal data that no other object would ever need to look at. I even talked about COMPLETE objects that aren't like tools, but more like finished products. In the real world, things like a washing machine or oven are very specialized applications. You can't really fit those in with anything else except in limited ways. A video player would be a good example of this in the software world. It's an application in of itself. And you can use it in your larger application (much like a washing machine inside a house). It does what it does and that's about it.

I've talked about that and fully agree that these things have a place. But they are not really in the realm of tools that let you build a bigger picture. Versatility isn't what they're good at.

Looking from outside of an FBP component, I don't know how pure functional or pure data a component could be. I just see input ports and output ports.



The ports and components are the functional parts. What goes through the ports and components are the data. Doesn't get more separate than that.

I strongly believe that the object should not be the only one that can provide functionality in order to manipulate it. This is a ruinousness idea.



If we have any disagreements, perhaps I should explain that the above quote is my central point. This is what I'm getting at. So the other stuff is perhaps my misunderstanding semantics.

To be clearer still, I think you are talking about reusing the component elsewhere as a subcomponent. I'm not talking about that. I'm talking about manipulating the same data in different ways by different components. In OOP, encapsulation frowns upon this notion.

There is no strong reason in FBP not to pass along fully functional components if this is a real need.



I don't like this idea. I'll allow it. But I REALLY don't like it. Every fiber of my being shivers at the thought of it.

Why don't I like it? It's fine if its inputs and outputs aren't connected and you just want to pass around different implementations for a particular interface. But in Project V, you can also pass around pre-connected components. I don't like this, but it's there. If "goto" is dangerous, passing around pre-connected components is like a nuclear explosion.

Dan Pologeadpologea Thursday, May 6, 2010 8:24:40 PM

Regarding the VCR, maybe it was not the greatest example. I have expected you to comment it, very good you did it.

Originally posted by Vorlath:

I don't like this idea. I'll allow it. But I REALLY don't like it. Every fiber of my being shivers at the thought of it.
Why don't I like it? It's fine if its inputs and outputs aren't connected and you just want to pass around different implementations for a particular interface. But in Project V, you can also pass around pre-connected components. I don't like this, but it's there. If "goto" is dangerous, passing around pre-connected components is like a nuclear explosion.


It is very interesting that you don't like it. This is in fact a rejection even if you have it in your project, still. I think that when you don't like something there is a problem with that something. Usually when I have had the feeling that something is wrong then there was a problem: with that something or with me. So the problem needs to be solved.
Passing a complete component (functional + data) that will have its ports dynamically connected later in the process of its processing is the only way of implementing the equivalence of the polymorphism in FBP. Somebody has to travel: the data that is about to be processed or the "processor" that will travel from one component to another like a honeybee from flower to flower. Both of them (the object processed and the processor) could be a mixture of data and functions. The connection will be dynamically made and after the connection is realized the data will flow to or from the component dynamically connected (that was an IP).
Again, I do not differentiate too much data from functions. This is the whole idea. Any kind of entity in FBP is a component be it pure functional, pure data or mix. The interface has the same syntax: ports associated with their types. The type is defined by the membrane (i.e. its collection of ports). That means a type is a collection of types, i.e. a membrane is a collection of ports that tell what membrane should have the objects allowed to pass through. In the end (lowest level, finest granularity) a type is an atomic component (pure data or pure functional). This is very important. That means any type is, in the end, a collection of basic types (pure data or pure functional). These types are implemented by the compiler and usually map to the processor types. The conclusion to this idea is: an interface (membrane) is, summing up, a collection of atomic components types that represent a part from component's subcomponents. But this part is exactly what gets out from a component through its output ports.

Vorlath Thursday, May 6, 2010 9:55:01 PM

A component is a pre-defined type. This is already complete. Data types are already implemented too. So yeah, you can pass anything that is a type through connections. The reason I don't like passing components around is that the connections become difficult to track. It's way more dangerous than spaghetti code. You could end up with basically a ball of yarn. If used judiciously, then sure, it can be of use. That's why I'm leaving it in. But people will abuse it. Count on it.

BTW, components have meta ports.

Maybe I can paint a better picture. Consider several layers of FBP networks. Level 0, level 1, level 2 and so on. A component is like a high rise where each floor is located on a different level. Some only exist on level 0. Some are two level buildings (level 0 and level 1). Some go all the way to the top. At each level (floor), you can define custom functionality that can control the level (floor) you are on and all lower levels (floors). For example, level 0 of a component can only deal with incoming and outgoing data. It cannot configure the number of input ports for example. But all higher levels can do this. So level 1 of an addition component can add a new input port when all current input ports are connected.

Level 2 and higher are for different things like error checking, retrieving implementations, network configuration, etc. You rarely have to go beyond level 2 (level 2 is error checking and caching).

Right now, the level that handles loading implementations is built in. It gives access to a meta port that takes as input a component implementation. So you connect the interface the way you want at design time, but at runtime, you can pass around different implementations and use whichever you like for your interface and decide as much at runtime on level 0.

So the upper levels give added functionality to the lower levels.

What I'm building is a dataflow environment like the world has never seen. Maybe I was too ambitious. I don't know. For now, all upper levels are built in. But level 1 will be available shortly after I have something up and running.

Write a comment

New comments have been disabled for this post.

June 2012
S M T W T F S
May 2012July 2012
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30