• Ingen resultater fundet

It is obviously worth considering if and how a programming language can be designed in such a way that it allows programmers to use their general compre-hension competences when reading and writing programs. The previous sections motivate certain directions for the design, given that we set out from the premise that human beings who are dealing with computer programs are rst of all op-timized for living in the physical world and then, as an afterthought, equipped with natural language and civilization, and nally, basically as a big surprise, immersed in a modern technological world.

5.2. CONTEXTUALITY IN PROGRAMMING LANGUAGES 107 The design directions include the following: Human beings are well-trained in exploiting contextuality, that is, in establishing mental models where exter-nal stimuli are not just understood by their immanent (stand-alone/dictionary) meaning, but are instead highly enriched by being interpreted in context of a model and then used to update that model. Similar techniques should be applicable when reasoning about computer programs.

Moreover, physical nesting seems to be a very fundamental and useful or-ganization of a universe of potential models, and it has the earlier mentioned nice property of making close models more relevant and urgent, and remote models more coarse-grained and stable. It is also desirable that programmers have support in organizing their understanding of program executions into such a structure.

Context dependencies in a formal system like a programming language must of course be based on a much more rigid and simple-minded mechanism than anything in a natural language. General block structure is one such mechanism which will allow words to derive their meaning from all the enclosing contexts, but a number of restricted versions of block structure are also being used in various programming languages. We briey describe them rst, for comparison.

The simplest approach is to use one large, global, unstructured universe con-taining all of the available entities, like in traditional FORTRAN, early versions of BASIC, or symbolic machine code (`assembler'). Thisdegeneratecase of context dependency support works nicely for small, simple projects, because there is no real need for complexity management, and because the lack of this kind of functionality allows for a simpler language.

However, as soon as the complexity rises above the trivial level the unstruc-tured universe becomes hard to manage, because every consideration about a given program basically has to set out from a total and simultaneous awareness of the entire program in full detail. The experience with programs of this kind or just programs of which some aspect was of this kind has given rise to the widely accepted rule that global variables should be avoided. The argumentation given here at least motivates the rule that there should only be few global variables.

The next step up is to support localized groupings of state, such as records, or behavior, such as procedures. When a given group of variables are accessed as elds of a record then the programmer is supported in thinking about this group of variables as an entity in its own right, and this alone reduces the complexity of the system by reducing the number of entities. However, it is only really useful if each usage of a variable from this group can be meaningfully explained in terms of the group as a whole, so in particular the group as a whole must be meaningful.

Similarly, a number of statements enclosed as the body of a procedure pro-vide for a simplication, compared to having the same statements on their own at the global level of the program. Again the composite eect of executing those statements should be meaningful as a whole, and each statement should make sense as a contribution to that composite eect. Apart from the fact that the statements can be understood as a group, it is also important that the number of possible execution paths involving such a group of statements becomes much

smaller, especially if the ideals of structured programming [116] are taken into

account. The core ideal here is that there should only be few and readily recog-nizable possible execution paths, and this is achieved by having such rules as:

each procedure should have only one entry point and one exit point, and the

gotocommand should be shunned [28] in favor of a few more specialized control structures (likeif,whileandforstatements).

Even more important, the notion of a procedure invocation is reied: The invocation of a procedure implies not only jumping to and from a group of statements, but also the establishment of a local environment which is specic for the given invocation.3 This makes it possible to receive arguments, to use local variables, and to return results; it also allows for recursion.

This notion of a local environment for each invocation of a procedure is an example (and an important one) of support for contextualitythe body of the procedure is executed in context of this environment, and since the environment is invocation specic, it is capable of supporting the modeling of a specic action or development which is an example, or instance, of the concept which is associated with the name of the procedure (assuming that the procedure has a name that corresponds to the eect of executing it, which of course it should have).

It is no surprise that procedures are the rst entities to have such a good support for contextuality, since they are so closely related to verbs, and verbs are so urgently dependent on their context. However, a verb phrase in a sentence is normally associated with a noun phrase which names a primary context for the interpretation of the verb, and then optionally an object which is being manipulated. Procedures support the notion of objects being manipulated, but not the notion of being contextually dependent on a less transient enclosing entity.

This level of support for contextuality corresponds to traditional imperative programming languages like C and Ada83. At this level it is actually possible to create and maintain quite large and complex systems, such as operating system kernels and, particularly using Ada, systems for real-time control of airplanes and weapons. Clearly, procedures and records are so useful that they must be taken seriously.

However, in recent years there has been a strong trend towards using object-orientation in many dierent application domains, and it is often given as a reason that object-orientation is somehow more natural for expressing human thinking than other programming paradigms, e.g. [24]. We basically agree with this line of thinking, but on the very specic grounds that it represents yet another step forward in the support for contextuality.

This step forward, in the mainstream of object-orientation, simply consists in allowing procedures to be dened as contextually located inside records. This is a technical change; the associated change in mindset and terminology is

pro-3It is often called an `activation record' and considered an implementation detail, but that is probably because it isn't a rst class entity in most languages; even the most implementation-independent formal semantics would have to specify it as a semantic entity, similar to other environments such as ordinary records

5.2. CONTEXTUALITY IN PROGRAMMING LANGUAGES 109 found and diers quite a bit between the user communities of dierent languages, but the (heretic) use of the old terminology allows us to see more directly what new possibilities it provides us with.

From the point of view of the Scandinavian tradition of object-orientation, this enables us to model a thing (a phenomenon associated with a concept covered by a noun) along with actions and developments that take place in context of that thing. Technically this is unimportant because the `thing' could easily have been provided to the procedures as, say, the rst argument. In languages like CLOS, Dylan, and Cecil, the typical notation for access to an entity inside an object (the `dot notation', as in myPoint.move) is actually explained as a mere syntactic convenience which in reality means that the object is the rst argument to the procedure. So objects are not particularly important in object-orientation after all:::

We believe that this entirely misses the point! The improvement in the un-derstandability of programs which can be obtained by supporting the execution of procedures in context of records (which is so profound that we rename it to things like `the execution of methods in context of objects' or `sending messages to objects') stems from the fact that contextuality is a complexity management mechanism which has been an inherent aspect of our approach to the world since long before the emergence of the human species. In other words, the im-portant dierence is in the support for our basic modes of thinking, and the fact that it technically may be explained away as syntactic sugar is an absolutely unimportant curiosity.

To clearly expose the reduction in complexity, compare the following sce-narios: Assume that we have a payroll system which models each employee as an object which has various methods for registering performed work and for receiving payments. In daily use the system is used to update the state of each employee-object to reect the amount of work which was performed by the cor-responding employee. Once in a while some money can be transferred to a bank account, and the employee-object is updated to reect that the payment has been made. A programmer working on such a program can work on basis of a mental image of an employee, and for each method on employees he or she can view it as an action which may depend on and/or change the employee in context of which it happens. The potentially large number of transient phe-nomona modeled by the methods are organized in context of the more long-lived phenomenon modeled by the employee-object.

In contrast, the connection between the transient phenomena and their long-lived context (the employee) has no direct support in procedural languages, so they give much less support to programmers for the context based complex-ity reduction. In other words, they emphasize each action or development in isolation, and that means that the collection of all actions and developments in a program execution becomes a much more complex whirlwind of isolated phenomena than it would be if it were organized into a number of object life stories.

Most object-oriented languages stop here, but in the Scandinavian tradition of object-orientation a more general approach is taken, as a consequence of not

abandoning but instead generalizing the support for block structure in Algol.

In particular, Beta supports a very homogeneous and general form of block structure, a generalization of the block structure in Simula, which is again a generalization of the language Algol. Note that the decision to abandon block structure in other communities seems to have been made consciously [23, 51], and this probably illustrates that general block structure used in an unstructured and meaningless fashion can inict great damage to the comprehensibility of a system.

The technical details of Betablock structure are the same as ingbeta, and they are explained in Sect. 5.3. The implications of this general block structure support at the conceptual level are unsurprising given the discussion so far, but we will briey summarize them as they represent the logical end point of the development: As a generalization of the previously discussed forms of block structure, general block structure naturally supports the modeling of actions and developments by means of procedure invocations carrying their own, local environment, as well as the modeling of actions and developments in context of noun related phenomena (things, persons,::: ). A usage of general block struc-ture which goes beyond mainstream object-orientation, but which was already present in Algol in the sixties, is the notion of nested procedures. They allow the expression of contextually dependent subactions which are used to construct the more complex enclosing action.

Moreover, it is generally possible to dene contextually dependent entities explicitly in context. For example, a student role can be modeled in context of the university to which it applies; an airplane ticket can be handled by a computer system using a model of an airplane seat in context of a particular ight; and an object inside agbetacompiler which is capable of generating code for a specic piece of syntax may be dened in context of the object which represents that piece of syntax, which may again be dened in context of the grammar for the language, in context of the compiler, etc. Whether an object is understood as a model of a real-world phenomenon or it is understood entirely as a computer based phenomenon in its own right, the important message is that programmers are capable of using their real-world comprehension capabilities to understand the semantics of programs, in particular by exploiting the contextual complexity reduction.

However, the rigidity of concrete programming language mechanisms, in particular when static type checkability is an important goal, sometimes makes it necessary to carefully weigh the gains in simplicity and consistency against the need for exibility for the handling of atypical cases.

For example, a project may be modeled in context of a company, and as a result the project may depend on the company (e.g. on the employees as modeled by their online calenders). The understanding of the life-stories of all projects can then be organized in context of the companies. Because of the regularities (e.g. that meetings for a given project are held in rooms belonging to the company of that project), the contextuality will ensure a basic and intuitive level of consistency that would not be supported if companies, employees, rooms, projects, and meetings were modeled in a at universe, as global and unrelated

5.2. CONTEXTUALITY IN PROGRAMMING LANGUAGES 111 classes.

Of course, a usable system must also be able to handle multi-company projects with meetings in third-party rooms. This can for instance be sup-ported by dening abstract, global, unrelated classes for meetings and so on, and then dening concrete subclasses in context of a company for the simple default case (meetings in the company's own rooms), and other concrete, global subclasses for the hard-to-handle general case (meetings anywhere, with par-ticipants from anywhere). This gives rise to more classes in the program, but it might very well be worthwhile to exploit the simplicity of the simple case also for users of the program, so that local meetings can be scheduled quickly in an simple dialog box, whereas scheduling of general meetings includes an inherently more complex selection of participants from multiple companies, as well as negotiation procedures between the various scheduling systems used in the involved companies.

There will always be a trade-o between hardwiring design aspects for simplicity and making them into parameters for generality. As an example of why parameterization should not simply be equated with `good' and hardwiring should not simply be equated with `evil', consider a language where a procedure can receive its body as an argument, e.g., as a list of statements. Now we can implement a given program with much fewer procedures, all we need to do is to provide the body (possibly as a result of a clever computation) as an argument to each procedure invocation. Wonderful exibility! However, the problem is that the manifest program entity, the procedure, has lost inherent meaning for the programmer who is trying to understand the program. It appears as an empty shell whose real meaning can only be understood when the infamous body parameter is known, and the choice of actual parameters is such a transient matter that it is hard to predict before run-time. As a result, we have seriously damaged the comprehensability of the program, even if we may have been able to reduce it to a smaller size.

Useful abstraction and parameterization mechanisms in programming lan-guages are those that allow a programmer to construct entities which are both meaningful and widely applicable. We believe that the ability to dene contextu-ally dependent entities is an example of a mechanism which allows programmers to reduce the exibility of the dependent entities in return for making the group of a context and its dependent entities more comprehensible as a whole. For example, a patternP inBetaand ingbetais specic for its enclosing objectO, dierent from the pattern of the same name in context of another objectO0even whenOandO0 are instances of the exact same pattern; similarly, the notion of a virtual or late-bound method in various programming languages implies that the meaning of the name of the method depends on the object in context of which the method is invoked. In both cases, the contextually dependent entity is in an essential way tied to the context, as opposed to the procedural equiv-alent where it is the same procedure that receives dierent objects as the rst argument in dierent invocations. Multiplication mechanisms such as instantia-tion of objects and incremental specicainstantia-tion mechanisms such as inheritance are then more important than ever, since they allow us to manage a larger selection

Global lookup of a name

N

in a view

P

:

Perform a local lookup in

P

; if that succeeds then the global lookup also succeeds, with the same result

as the local lookup; otherwise nd the view of the enclosing object of the frontmost mixin in

P

, and perform a global lookup of

N

from there;

if there are no more enclosing objects then the global lookup fails

Figure 5.1: The rule for global lookup of specialized variants than would otherwise be practical.