On 21 Apr 2017, at 02:44, Tim Nevels via 4D_Tech <4d_tech@lists.4d.com> wrote:
> I think we sometimes forget that 4D is not a 3GL programming language like C > or C++. 4D is a 4GL language. You don’t get all the feature, benefits and > capabilities of a 3GL language in a 4GL language. Remember why we all use 4D > instead of using C++. You can get so much more done with 4D in less time and > with less programming effort than doing it in C++. Amen to that ;) What a thread ! Great discussion. I feel I must make at least an attempt to convert David’s component-pain to pleasure by recontextualising some of the interpretations of the archetype. Of course everyone has their personal priorities and preferences so it’s just my take, but I have spent 30% of my living existence doing components so I’ll get these points of my chest before any more of it passes me by (Life that is). In general, I find 4D’s component model immensely powerful - still far more productive than its 3GL equivalents and there isn’t a lot I would change about it. I haven’t read the whole thread but some general points that spring to mind are these: FLAT vs HIERACHICAL METHOD TIERS In my view, 3GL - 4GL distinction isn’t to do with language, it’s to do with complexity. In a 4GL (well at least in 4D’s implementation) we are always working within a unified top language tier which is flat. It’s therefore right that the developer should have to disambiguate between method names amongst components because the whole point of the component is that it provides a seamless extension to the host (flat) method namespace as opposed to some kind of hierarchical class-based extension. DESIGN TIME vs RUNTIME ARCHITECTURAL CONTEXT As such, it’s not comparable with say a class library or a DLL or other type of 3GL code module since 4D components are only given an architectural context at runtime. This is a huge advantage over traditional coding structures because when we design and build components we’re always working in a host context. When we’re developing a component, we should not think like a component developer, we should think like a host developer because 4D will do the architectural contextualisation for us at runtime. If you start to think architecture when you’re building components you’ll get very confused because there is no architecture at design time. There are of course odd exceptions to this but a tiny number and they *are* exceptions, not the rule (See “NASTY STUFF” section at the end of this ramble). NAMESPACES Remember, 4D owes much of its productivity to implicit referencing, with the proverbial elephant in the room being of course the datafile itself. Current selections are implicit (within the scope of a process), globals are, methods are, form scripts are, form objects are and resources are to name but a few. There are no explicitly referencable namespaces anywhere in the language and the reason there aren’t any in components is because it would significantly disrupt this highly productive paradigm for very little gain and probably a measurable productivity loss. As Tim says, it also conflicts with 4D’s identity as a 4GL. CASCADING DEPENDENCIES Contrary to some of the concerns I’ve seen expressed, you can have components call other components in a hierarchy without any problem. In fact you can have 2 types of coupling in that regard: 1: Tight Coupling (Where a component references another component’s method explicitly). Advantages: compiler is made aware of all calls, compiles as reliably as if all methods were host native calls. 2: Loose Coupling (Where EXECUTE METHOD is used). Advantages: components can be ‘unplugged’ without any compiler complaints. The optimal choice can also depend on short/long term criteria. If your component is going to be used all over the place and potentially only occasionally, loose coupling can be better. If the component is a permanent resident (e.g. a bookkeeping library in an ERP) then I find tight coupling far more reliable. DESKTOP CONFIG: ALL AT THE SAME LEVEL The fact that a component doesn’t "on board” its dependencies is a GOOD thing, not a deficiency IMO. It confines the dependency hierarchy to the logic, not where they happen to be located on the desktop and again reflects the 4GL paradigm that all components are ultimately an extension of the host top layer. You’d end up with a version control nightmare if you had to install the same component in 6 different siblings instead of just once at the host level. So again, my view is that 4D have thought this through to give us the best of all worlds: • We have cascading hierarchical dependency supported • We have optimal desktop configuration • We have a choice of hard or soft coupling RESOURCES How 4D handles these is a thing of beauty IMO and is worth including into the overall architectural appraisal. When I researched this for my summit presentation I actually blew myself away as to the elegance of it because 4D manages to deliver much of a 3GL flexible model but with 4GL productivity. Consider this: published methods are common, picture resources are not, custom constants are common, language resources are not. The result of all this architectural weaving is a totally transparent host/component development experience because even if you have an extremely complex multi-lingual X-LIFF based, custom-constant based, fancy graphic laden structure, you don’t need to do one bit of extra work to implement it as a component. Its constants will appear in the host - just like a plugin, it’s language resources will be mapped appropriately from the method context and form resources will similarly be remapped to the component’s resource folder instead of the host’s. How resources are handled is another hint as to the archetype we should appropriately interpret - a 4GL type extension of the “flat” host layer rather than a 3GL type subordinate level in a class hierarchy. ERROR HANDLING I notice David, you were having issues with this, but I think you may have gravitated straight to the exception that’s the most footery aspect of components. (If you had been eating a lobster, it would be like picking of one of those tiny end-claws and trying to get the meat out of it rather than going for the body flesh first :-)). Ok, components do not have access to each other’s Error variable. So each component needs its own independent error handling. (Thats why I said ’think like a host, not a component’). HOWEVER, if you want to be fancy about it you can write a component utility that does error handling for all memory spaces, you just need to provide it with a callback that allows it to grab the “Error” value, described below in more detail. NASTY STUFF (Only really needed if doing lots of multi-dependent component work, otherwise forget) Actually not so nasty, it’s just that its not as intuitive as everything else and is the ONLY place where you need to think like a component developer. The single biggest pain with components is knowing which component is calling it. Usually you don’t have to know this because the host just passes a pointer and then the caller is implicit. But there are occasions where you do, e.g. when a callback is needed but there may be multiple components that use us. (“Us” being the base component utility that’s doing something useful for subscribing components). Suggested ways to deal with this: I have 1 base utility component that all other components always are dependent on. (It has about 1000 methods or something). Includes Error handling, form object utilities, query stuff, you name it. It has a counter which is zeroed at startup and increments whenever a dependent component wants a “unique identifier” which it uses throughought the session to tell the base component “this is me". You can use a more decoupled way to get the identifier, e.g. UUID or just the component name itself, doesn’t matter. Then there is an array table for callbacks. So, lets say a dependent component that does sales tax processing wants to register itself with the utility component. It calls in at startup, gets itself an ID, supplies its various callbacks for error handling, form parsing, “Get Pointer” proxies, etc and these will form a row in the utility component’s callback registry that can be accessed using the registered ID. If no ID is provided, the utility component assumes the subscriber is the host (ID=0). In 3-tier cascading dependency therefore, (HOST -> SALTAX -> UTILS) the centralised error handling call from the SALTAX component would look like this: UTILS_ErrHandler(1;saltaxME) SALTAX_DO_AnERRORproneTHING UTILS_ErrHandler(0;saltaxME) (saltaxME returns the component identifier ID understood by both the subscribing component and the utility component. If SALTAX was in host mode - i.e. in development - the call would be exactly the same). Here, UTILS_ErrHandler does 2 things. UTILS_ErrHandler(1) installs the SALTAX_ error handler in the SALTAX_ module. It knows what to call from the subscriber callbacks lookup table, so it actually calls SALTAX_ module and tells it to install its own error handler. Then, if an error does occur, the SALTAX_ handler calls back to the UTILS component to report the error number. The UTILS component handles it centrally from then on (using a common form, messaging etc). The second call removes the error handler off the ‘stack’ for the subscribing component. So here, the utility component is using callbacks to manage independent error handlers for ALL memory spaces (subscribing components) but handling the response to each error itself. I know this may slightly horrendous but it’s actually simpler to implement it may sound as long as you do it in a “lab environment” - plain pair of vanilla structures with hardly any other code in them. It has one huge advantage - you only ever need 1 error handler when you’re working with lots of components. After that, no need to worry about the context (in which component the error occured) so one can go back to being a host developer ;) WHAT DO WE NEED CALLBACKS FOR ? In 15 years of component developing, here’s a comprehensive list of the only callbacks I’ve ever needed: - FORM GET PROPERTIES - Get Pointer (for both objects and fields in a (Table;Field) synta) - MENU BAR handling - GET PICTURE FROM LLIBRARY - ON ERR CALL Hope that helps…and to David A., I hope you manage to stick with it long enough to write another “Joy of Triggers” episode someday ! -P ********************************************************************** 4D Internet Users Group (4D iNUG) FAQ: http://lists.4d.com/faqnug.html Archive: http://lists.4d.com/archives.html Options: http://lists.4d.com/mailman/options/4d_tech Unsub: mailto:4d_tech-unsubscr...@lists.4d.com **********************************************************************