All life is problem solving
(Karl Popper)
Have patience. All things are difficult before they become easy
(Moslik Saadi)
It's time to finally overcome the antediluvian technology of software production using text editors. Programs should no longer be "written" but constructed/composed in Lego-like fashion from basic constructs, using structure editors rather than text editors. Particularly the executable portions of programs are the last bastions of textual programming that remain to be captured by "point-and-click" technology.
Structure editors are also a valuable and almost indispensable basis for the realization of powerful restructuring / refactoring operations, which should be supported by every modern IDE (Integrated Development Environment).
Older, non-object-oriented languages like Modula-2 and Ada, that were based on "abstract data types", had already achieved a clean syntactic and semantic separation of "interfaces" and "implementations". It's high time to regain this very important achievement in object-oriented languages in a comparably clean way. The distinction made by Java between interfaces and (abstract or concrete) classes does not yet fully achieve this strict separation, since the type of an object may be both an interface or a class in Java. Moreover, Java interfaces are groups of public functions only. They are unable to expose public data members.
You can, of course, provide GET and SET access functions as a substitute. But even then we should prefer 1. to hide these access functions in the implementation of the respective interface, 2. to expose the respective property (for instance the "account balance") in the interface (perhaps protected by a "read-only" attribute), and 3. to allow the programmer to directly refer to this member object in assignments or read-references, rather than to explicitly call access functions.
It's time to overhaul the object notion itself which would presently be used to describe two conceptually very different categories of things, for instance bank accounts as well as money transfer orders:
A new object-oriented and component-aware language should clarify the relationship between program-internal objects and external component objects that "live" in address spaces of their own and that are possibly implemented in other languages. It should provide unified language constructs for describing and using the interfaces and implementations of internal as well as component objects. Based on these constructs, it should be easy to incorporate in particular COM, CORBA, and JavaBeans objects. "Intelligent" interaction of components (compound documents, embedding and linking of user interfaces, drag-and-drop, clipboard,...) should also be supported. Databases and files should appear as special (persistent) component objects having specific interfaces.
A basic requirement of a properly integrated component notion is that your programming language should clearly exhibit the difference between state objects (that can only be passed by reference to the methods of components or "external/remote objects") and value objects (that may or even must be passed by value, without corrupting their semantics).
Another requirement, in this same context of parameter passing, is that your language should allow you to clearly delineate the "borders" of objects that are to be passed by value. This amounts to a proper distinction between "constituents" and "acquaintances" of objects (see below).
A modern object-oriented language should properly support design-patterns / frameworks since they provide new, additional ways of software reuse. We need a way to collectively specialize groups of parameterized classes. Single-class macro constructs, like "templates" in C++ or "generic types" in Eiffel won't do.
We generally believe that templates / generic classes and their conformance rules in C++ andEiffel are too hard to manage and to comprehend. They tend to produce almost incomprehensible "template libraries" which are very powerful in principle but far away from providing easily and readily reusable designs.
The conventional perspective on "templates" or "generic classes" is that they are a kind of macros that have to be "instantiated" (meaning text substitution) in order to become concrete classes that can be used to specify the types of objects. In order to overcome the apparent problems inherent in this view (complex nested instantiation expressions, complex type conformance rules, code replication with huge storage requirements) our Lava design proposes a transition from this "macro instantiation view" to more unified and universal class and package notions including parameterized classes and packages (roughly = groups of classes) and an equally unified derivation notion that covers also to the [collective] specialization of parameterized [groups of] classes.
Type casts would be used primarily as repair measures in cases where the programmer knows that the run time type of an object is actually more derived than the static type of the containing variable, and he/she needs to use features of the more derived "dynamic" type. Type casts are not only ugly, nasty, and annoying but much worse: their justification can only be assessed on the basis of a more comprehensive understanding of the dynamic program behavior in general. Possible reasons why type casts are used nevertheless or may even be inevitable in contemporary statically typed object-oriented programming languages:
- You cannot specialize parameters of methods on class derivation.
- You cannot specialize data members of a class when defining a derived class.
- You cannot express collective specialization of several classes that refer to each other.
In all these cases you know at programming time that in the derived classes (or group of related derived classes) certain member variables or function parameters will actually be of a more derived type than in the corresponding base class(es) but the derivation mechanism of your programming language does not allow you to express this. So you retain the original types assigned to these variables in the base class(es) and resort to type casts in the derived class(es).
The "variable" notion of present programming languages would view a variable as a "data container" to which you can again and again assign a new content/value ("multiple assignment"), and to reference a variable means to reference its "current content". But in order to correctly locate the origin of its "current content" you must exactly understand the dynamic control flow of the program. If you admit "global variables" that can be accessed from anywhere in the program then this will mostly become even more difficult and error-prone .
We expect that abandoning "multiple assignment", the conventional "variable = data container" view, and particularly "global variables" will greatly clarify the data flow of programs. It will be much easier then to locate the origin of values. The "single-assignment" rule reads:
You just need to follow the current program branch statically in upward direction to find out whether and where a value has been assigned to a variable that you are about to reference. This analysis can be performed automatically at programming time.
A particular consequence of "single-assignment" is that conventional sequential loops that forward information via some variables from one pass of the loop to the next are excluded and have to be replaced by recursive function calls. In our opinion this is a desirable step towards a more mathematical semantics and understanding of programs and contributes to the clarification of data flow.
"Non-recursive" loops, however, (whose passes could in principle be executed concurrently,) may be expressed by "foreach" and "exists" quantifiers running through finite sets of objects.
If you would like to retain a copy of a complex object that might be subject to further changes in the future, or if an object shall be "passed by value" to some function, or if yu want to compare complex objects (member by member) then you would like to know which of its members/attributes represent real "constituents" of this object and which are just references/pointers to other, independent objects. In other words: you would like to specify the "borders" of complex objects, or to express what "belongs" to an object and what are just "acquaintances". Particularly when crossing component borders you have to make such a distinction, as is well known from DCOM and CORBA. A component-oriented language should take this into account.
You must not confuse this logical, application-level distinction with the implementation issue whether an object and its constituents are all allocated within the same contiguous block of storage ("embedded constituents", as in C++ objects and the "expanded classes" of Eiffel), or whether constituents are linked by address pointers in a low-level sense to their "owners". A programming language may very well support both "linked" and "embedded" constituents, while "embedded acquaintances" would be a rather contradictory notion and would not make much sense.
Summary: Neither the low-level address semantics of C++ pointers nor the complete absence of an explicit pointer notion in Java are satisfactory solutions but we need a proper distinction between "constituents" and "acquaintances" of complex objects, irrespective of the question whether constituents are implemented as embedded or as linked constituents.
Inadvertent use of uninitialized data frequently causes programs to crash in a way that is hard and time-consuming to disentangle, which in turn gives rise to immense costs. So it's time to undertake really serious efforts to prevent such situations wherever possible by static and run-time checks. Static initialization checks are preferable, of course, but will require a fundamental revision of expressive means for executable code in order to make these checks as complete/comprehensive/effective as possible.
It's time to overcome the painful dissociation between programming languages and database languages ("embedded SQL"). Many people have got accustomed to this schism to a degree that they deem it to be a law of nature. But in practice it is highly unproductive to learn two languages and to bridge the syntactic and semantic gap between them again and again. The increasing importance of object-oriented databases is a strong motivation to look for more unified expressive means for data definition, data manipulation and query, particularly for searching and accessing complex container objects, no matter whether they are persistent database objects or transient program-internal objects.
The purpose of graphical user interfaces is to present a graphical representation of selected internal application data structures to the user and to allow her/him to manipulate these data structures by point-and-click and key-press operations. Present UI builders work on a very low level and require a great lot of manual work to establish this mapping between internal data structures and external visible representation and to add user interaction handlers to this representation. UI builders are big and complicated tools and they have little or no knowledge of the actual, complex application data structures. The programmer would have to construct intermediate auxiliary data structures that are required by the UI programming interface.
Presently this entire UI construction process is very costly and very cumbersome. Our idea is to replace this process by a more direct, semi-automatic and annotation-controlled mapping from complex application data structures to complex visual representations: Annotations to the components and elements of an application data structure would specify the details of the associated visual representation, and a kind of "user interface interpreter" would read these annotations at run time and constructs the visual representations.
Lava solution: Under development.
Transaction programming, multi-threading, inter-process communication / synchronization, distributed processing are inexhaustible sources of errors, frustration, and intricateness that should be drained finally. It would in particular be an invaluable advantage if you could handle all these problems in a purely declarative high-level style, i.e., without needing any delicate low-level executable transaction, communication, and synchronization primitives like start_transaction / commit / abort, or semaphores / monitors / mutexes / conditions / events / send / receive / wait. Transaction abort should appear as a quite normal case of throwing an exception.
A language with all these characteristics should also be organization-aware: If you can talk about interfaces to persistent component objects then you should also be able to talk about object ownership in terms of persons, or more generally: organizational units (OU's), of the application environment. Likewise, it should be possible to specify OU's that are to be contacted via some display by interactive components of an application. Data ownership and contact addresses for personal interaction need not be specified in terms of one single organizational name space. The various types of conventional addressing schemes currently in use should be supported equally well, like file and computer names, ftp, http, e-mail addresses.
Lava solution: For future research.
Once you can talk about data ownership and interactive responsibilities of OU's it is obvious that you would also like to talk about security rules and policies. You would like to restrict the access to persistent data and the invocation of services, and in addition to these "preventive" security measures, it should be easy to integrate "defensive" measures (using digital signatures and encryption) into applications.
Preventive and defensive security is not just a matter of providing proper library functions but should be supported directly on the language level:
Lava solution: For future research.
After all, the Lava development can be viewed partly as an attempt to capitalize on the gaps and omissions inherent in Java, Visual Basic and other popular contemporary programming languages.