Aspect Oriented Programming Type Implementations

Forums Personal Topics Unbidden Thoughts Aspect Oriented Programming Type Implementations

This topic contains 4 replies, has 1 voice, and was last updated by  Josh Stern February 14, 2023 at 3:50 pm.

  • Author
    Posts
  • #126177

    Josh Stern
    Moderator

    Many enjoy the aesthetics of mathematics & a view of programming types as mathematical systems.We don’t wish to nullify the validity of that view, however we believe that the mental design space of virtual realities that reflect our abstractions of the infinite variety of real world problem domains and their dynamics will rarely take one step from those conceptions to a PL type, to fast execution in a modern CPU. In domain modeling, we notice varieties of “individual elements/atoms” that persist in some domain of interest for some length of time, with a multitude of static & dynamic individual properties & relations between individuals/properties. In our heads, we may describe parts of those conceptions as “The information that ____ .” When we implement computer programs, we mananage computing over those models in ways that are comfortable for software engineering. Mapping between “The information that____” and PL types are not static or 1-1. The role of the program is to correctly preserve and compute with the VR system that makes up a given application. We should think of the software enginering/CASE job as providing the best & most flexibile accomodations for doing *that*.

    How to develop in that direction? Using Abstract State Machine & related, enhanced logical mechanisms to model coordinated parts of VR has been discussed elsewhere. We can imagine guided tool support for stating which elements of an ASM model or a UML model are selected as objects in a given program. Then we can imagine a different plane of UML that describes markov chains of various transitions a given object can undergo in terms of how information from that object is temporarily represented in a program. We can check the homomorphisms & isomorphisms that are required. Then compilation has a guide for what is allowed & what may make sense in particular computational sections of code (enhanced, by profiling or other empirical/theoretical mechanisms). And source level debuggers can be enhanced to be aware of the mappings that were selected.

    Many people don’t like to get bogged down with CASE tools as general practice, so what is the lightest weight version of the above – just giving an okay for particular data transformations as a meta-compilation choice. Better is to also allow semantic constraints expressed in an information representational language to be preserved in blocks of code even when the data was transfomred (as if objects with information hiding had implicitly implemented those guards & constraints in code). If the mappings that are used are efficiently generated, then it can be checked in normal course of compilation. That should be the case. Our semantic language/NLP interface is still underdeveloped at the other end of the work problem. But GT, I believe, has done some work in that direction.

  • #126202

    Josh Stern
    Moderator

    Re: Implementations of Name Overriding – One often sees name mangling of overriden function names, automatically generated for different type conditions, can become very long strings. The human factors on the strings are not too helpful in most cases. For an environment that might construct either a binary or byte code version of some overridden function, it may be worth considering whether implementation of the schemes with the most efficient run time behavior & separate mechanisms for adding human readability. Perhaps assembled programs map to atoms that are directly hashed, so static assignment can be the static section of the atom table & the dynamic section only has to lookup the hash once in most cases.

    For implementation of ASOP, it’s desirable that binary & byte code compiling can do late binding with multiple dispath of functions. For OS/machine interaction, it’s desirable if there is a way to do that efficiently without making the pages with code generally writable – mmodifyThisList(…,…) or something

    Edit: Another implementation idea to consider – functions without external linkage may adopt their own calling convention that could be, for instance, a single stack frame oriented record for each functiont hat includes the arguments but also the info on how to look up the late bound versions of functions & meta-object info. This might be a win as a means of implementing late binding, and also as a way of keeping a small footprint in fast memory since the far flung static records of each class would not need to be accessed or have their vtabes scanned. Can be just integer-like hashes to lookup the stuff being actively used in the call context.

    Thining about C++ implementation virtually helps to visualize it. The dynamic runtime info about an object can be summarized as an atom describing the index of the object layout in a class hierarch & an atom describing the virtual function table index – the rest of the info is statically available.

    In C++, those are normally fixed at object construction time. But we can imagine ASOP code that modifies it in allowable ways at other times – special additional flexibility without copying if global compilation leaves extra space in certain object creation instances, but many types of ASOP changes don’t need that.

    Based on that program wide concept of atom lookup, we could also have localized/compactified versions with atom schemes that are relative to those versions, with the affect of implementing the late binding, etc.

    Edit: this technology would also make it easier to optimize fast memory footprint at runtime for pure performance, rather than late binding reasons. Wherever the hit from hash lookup of first function call reference inside another function is << %chance increase of extra memory page load needed then it might be helpful. Edit: The hidden call records supporting late binding could also include the resolved hash indexes for the objects that implement emitting signals & links to receiving slots. This would make it easier to achieve essentially no overhead where those mechanisms are not being used (something analogous to a null pointer signal) and make it easier to coordinate sending the signals to distributed network apps as the late binding function. It should be straightforward to implement a type-checking slot receiver for an individual object's needs rather than always requiring the class level. Similarly, the name of an emitter doesn't have to be an object pointer or a class name - it could be some other brand string or atom. Add:Thoughts on efficient implementation of late binding signals/slots a)Static Class areas can hold info about 1st, whether class objects have been used for emitting signals or for receiving slots - 1 check which can be once per function or optimized away if the answer is know. Then within that, a quick lookup of which fuctions emit and their atom contribution to hashing. At runtime, receiver objects are put into lists for the emitters that they are connected to, which can be either any class object & function or a specific object & function. Lambda/anon function handling can also be supported. The overall penalties should be very low. Our Qt expert is even interested in testing what the savings might be (although there is a lot of visible source code with the existing style that would require tool support to move).

  • #126209

    Josh Stern
    Moderator

    Supporting parametric controls of aspects across an entire code base apparently requires that each relevant function & object be parameterized by a finite set of aspects that might be specified as relevant to its final implemented form. Automatic methods can generate the final form from a template if the generative recipes are simple and the hooks are easily put in place by the person or meta-programming tool authoring the given methods and objects. CASE tools can also be implemented to look for places where the hooks were left out for no apparently reason – there is intelligence involved with that – is the position inside of a loop of some sort? Some aspects may have a pre-loop/after-loop and pre-code block/after code block form as preferred alternatives. A function which is specialized to implement a given aspect may be either sub-optimal or incorrect if particular function arguments don’t have types which match the aspect choice or take on incompatible parameter values. Compilation failure or warnings is the correct response in those cases. This position helps us see the need to be able to add a given aspect value to many/most types of objects.

    Generally speaking, the metaprogramming environment/preprocessor should be richer and more syntax oriented than what most languages natively provide.

    For all key aspects of interest, and each language in focus, there ideally should be a schematic catalog of where any given object stands in relation to the set of possible values the given aspect can take on – including, of course, the default condition of a given language – or special reasons why there is an incompatibility.
    Ideally every aspect take on other values without interacting with other features of the object, or only interacting in parametric ways – e.g. the size of the object increases. But there harder cases where that isn’t possible – perhaps then there is a interaction between pairs of aspect feature states.

    The higher level sense here is that design matters a lot & the initial focus should be on the most interesting/valuable choices of aspect oriented control for large code-bases and how they combine.

    Some potential areas of interest:

    Concurrency Coordination
    Distributed Networking
    Logging strategy/state
    Memory usage strategy
    Exception handling strategy
    Information hiding strategy
    External linkage strategy
    Native code vs.byte code vs. other strategy
    Adding extra work/arguments in loops/function calls
    Inversion of control for specific parameters strategy
    Data persistence strategy (i.e. details of location (db,files,etc) and serialization/deserialization protocols for efficient auto-generation)
    Nature of “const” status (deep?)
    Safety for passing to opaque legacy function calls?

    Some types of ASOP can be implemented entirely inside of a class interface while others depend on the cooperation of external function calls. In the latter case, aspects can be treated like a special variety of extra typing requirement – e.g. like adding “const” in C++ – functions need to specify in their interface and be implemented in a compatible way. If a type doesn’t allow passing to opaque legacy code then those objects can’t be used in arguments to such code.

    Something like this then:

    Object o1; //might or might not implement DoesGobbyhere

    {
    insureAspect(DoesGobbly,o1);
    f(o1,o2); // first argument must include DoesGobbly if it is that type of demand aspect.

    } // insureAspect demand goes out of scope here

    Which facilities would improve future programs or make them easier to create?

    Platform Dependence Conditions

  • #126275

    Josh Stern
    Moderator

    Useful excercise from a C++ flavored target persepctive:

    class Ptr {
    private:
    T* the_ptr;
    public:
    Ptr():the_ptr(0) {}
    etc
    }

    I don’t mean to restrict code to C++. The point is to think about a fairly comprehensive cross-cutting set of concerns for how the value of ptr can be set,accessed,modified,stored to persistent storage, mutability vs not, transmitted across a network, etc. Some aspects may add other body to the set or use a static class area. Maybe the start was wrong & we don’t want to fix the actual point right away by default. In any case, we want to think about the types of phase life cycles for a reference and how they vary in needs/restrictions & how the transitions would be implemented, so that type checking & type inference support the correct engineering.

    Aspects that can vary include:

    kind of memory that holds object & what it references at shallow & deep levels

    concurrency concerns

    design issues for lifecycle, persistence, and possibly portability

    potential amount of resource control

    finalization strategies including exception unwinding

    possibility of class/type overrides

    what is the level of meta info available/needed at run time?

    Edit: Do we have a way to write a meta-program that describes different design plans and asks whether 2 or more plans are consistent in the same program and whether they can use the same implementation for a given set of aspect needs?

    Q: In what space does the solution live? Is it part of a special metaprogramming language?

    A: I’ve advocated metaprogramming languages that use partial specifications of programs/functions/hardware/data layouts etc. and relationship to detailed domain models. But I can’t see the state of that work or the directions it has gone in. The goal of controlling aspects from high level source is a field goal that many will try and address in various ways, including IDEs, and new programming languages in addition to metaprogramming approaches. I meant to focus on the pointer issue as a challenging testbed that touches many of the hardest issues without a lot of misc distraction.

    Q: How does it relate to work inside of a compiler?

    A: All the solutions should care about the ease and effectiveness of compiler optimizations when their targets include compilation. C++ is already a complex machine with stack unwinding and various hardware specializations, but it is not as expressive as the set of cases that can be already treated by LLVM’s internal metal language. For that reason, I like LLVM as a direct target for new language implementations & issues of what can be coordinated with compilers.

    Also consider that the set of benefit cases for JIT rruntime optimizations increases over time & the set of permissible optimizations depends on the design/type hinting available to the compiler, either explicitly from a programmer or more commonly in the actual specification of a given language. Pointer/Array aspects that the compiler understands will generally permit a greater range of permissible optimizations to consider, which may then mutate at hot spots recognized at run time or via global optimization/profiling.

You must be logged in to reply to this topic.