• Ingen resultater fundet

Building the extensible spreadsheet in Beta

The problem then, is to write such an extensible spreadsheet application and to identify what special language mechanisms were used to accomplish this task.

Computer hardware is designed to be highly extensible. This is primarily because it is not very easy and economical to produce completely new hardware each time an extension has to be made. What aspects of hardware make it so extensible? It is primarily the overall architecture which comprises of a mother board with slots. Much of the extensible functionality resides on extension boards which are plugged into these slots. Users can typically handle the following tasks themselves:

add a new board in an empty slot

specialize an existing board (e.g. add new memory SIMMs)

replace an existing board by a newer version

If software could be constructed in this manner, similar benets could be availed of. One way to look at it is as follows: the mother board of a program is the main program and the slots are open points (e.g. callbacks in many languages). The extension boards are the various software components (classes, procedures, functions, types, ...) which ll the open points. This structure (of mother board, slots and components) can be replicated within each software component.

Extending a software system can then be viewed as a process very similar to extending a piece of computer hardware. With software, in addition, to just swapping boards, it would also be possible to allow users to construct new boards (generally by modifying existing ones). It may also be possible to support modications of the mother board.

The development of the spreadsheet application in Beta follows this theme: it is con-structed in the manner prescribed above. The construction is presented in stages | primarily to illustrate all the features without too much complexity. In the rst attempt, is presented a version which doesn't allow dynamic extensibility. It serves to introduce the general framework of the application and acts as a reference with which to compare the dynamically extensible version. The second attempt presents a version which can be dynamically extended.

Appendix A presents a brief introduction to Beta.3 It is recommended that the reader glance briey at it.

3For a detailed description of Beta see [KMMPN87, MMPN93].

2.3.1 The rst attempt

Here is an abstract presentation of a spreadsheet system. Only details relevant to this discussion are presented. SpreadSheetSystemis declared as a Beta pattern; nested within it are declarations of Cell, TextCelland PictureCell; the latter two are sub-patterns of the rst. A few variables, Row etc., are declared. Finally, there is the do-part which is the body of SpreadSheetSystem; it gets executed whenSpreadSheetSystemis executed; it creates instances of TextCelland PictureCell.

Listing 1

.

SpreadSheetSystem:

(# Cell: (* Cell pattern *)

(# loc: @Position;

draw:< (# ...INNER... #); (* ':<' means virtual declaration *) edit:< (# ...INNER... #);

update:< (# ...INNER... #)

#)

TextCell:< Cell (* TextCell specialization of Cell *) (# value: @Text;

(* '::<' means further binding of virtual *) draw::< (# do ... textOutput value; INNER ... #);

edit::< (# do ... invoke text editor on cell; INNER ... #);

update::< (# do ... recalculate value; draw; INNER ... #)

#);

PictureCell:< Cell (* PictureCell specialization of Cell *) (# value: @Bitmap;

draw::< (# do ... bitmapOutput value; INNER... #);

edit::< (# do ... invoke bitmap editor on cell; INNER ... #);

update::< (# do ... recalculate value; draw; INNER ... #)

#);

Row: [100]^Cell; (* array of references to Cell objects *) Sheet: [100]^Row; (* array of references to Row objects *)

aRow: ^Row; (* reference to a Row object *)

do ...

Sheet[10][] -> aRow[]; (* Get and store REFERENCE to row 10 *) (* create a text cell at position (10,10) *)

new(TextCell) -> aRow[10][]; (* ref to new text cell in col 19 *) aRow[10].edit; (* invoke edit pattern on this new cell *) (* create a picture cell at position (10,11) *)

new(PictureCell) -> aRow[11][];

aRow[11].edit;

aRow[10].update;

...

#)

To run this spreadsheet system, one must create an instance of SpreadSheetSystemand execute it in some runtime environment.

Listing 2

.

main:

(# SpreadSheetSystem: (# ... #);

aSpreadSheet: ^SpreadSheetSystem;

do ...

new(SpreadSheetSystem) -> aSpreadSheet[];

aSpreadSheet; (* execute the system *)

...

#)

If this were a real implementation of a spreadsheet system, rather than just an illustrative one, the do-part of the SpreadSheetSystemwould invoke some user-interface loop which would present somestandard spreadsheet-like interface. Statementssuch asnew(TextCell)

etc., shown being executed directly in the do-part, would typically be executed indirectly through this user-interface. As the user-interface aspect is not relevant to this discussion, it has been omitted for the sake of simplicity.

When mainexecutesSpreadSheetSystem, by creating an instance of it and executing that instance, control is in the do-part of the spreadsheet pattern. Thenew(TextCell) creates an instance of the TextCell pattern and stores a reference to it in position (10,10) of the sheet. When the method draw, for example, is invoked on this instance of TextCell, execution begins in the draw method declared in Cell; when it hits the INNER construct it enters thedrawmethod declared inTextCell. Upon completion of this, control returns to the draw method in Cell at the statement following the INNER. The same applies for the other virtual methods in TextCelland PictureCell.

In this implementation of the spreadsheet system, the following language mechanisms are utilized to implement the system so it is extensible:

Block Structure.

Block structure allows one to construct a software system as one would construct a hardware device i.e. as a number of sub-units (boards or components) which are part of a larger unit and so on. As in a piece of hardware,

there are component boards, in a software system there are blocks. Nesting of blocks, as in Beta and Simula, allows one to model the building blocks of a system at a coarser level than just ordinary unnested classes. It also serves to package together related functionality. For example, the SpreadSheetSystempattern models a spreadsheet building block and the TextCell pattern models a text-cell building block.

Once a system is structured in the form of such building blocks, making extensions is conceptually easier: replace the appropriate building block by an extended version.

This is similar to what happens in the hardware world: a computer's networking ca-pabilities are extended by replacing its existing ethernet board with a newly released version.

Virtual Patterns.

A virtual pattern allows one to model a component that is open i.e. has some deferred functionality, yet, possibly has some default behavior. They are essential if one has to write components which must be extensible without any changes to their original denition.

In Beta, patterns unify types, classes, procedures, and functions. As a result, the concept of virtual pattern in Beta, spans all of these related concepts of virtual types, virtual classes etc. Sandvad and Nrgaard, in their report [NS90], show how virtual patterns can be used to defer actions, values, and substance i.e. declare components in which these aspects may be specied later or their existing denitions specialized, all without modication to their original denitions.

In the example, Cell is written with virtual procedures (deferred action): draw,

edit, and update. In addition, SpreadSheetSystem is written with virtual classes (deferred substance): TextCell and PictureCell.

Further binding of virtuals in all specializations.

In Beta, unlike Simula and most other object-oriented languages, a further binding of a virtualdoes notoverride the original denition or previous bindings. For example, in SpreadSheetSystemin the declaration ofTextCell, the further binding ofdrawdoesn't override the original declaration of draw in Cell; it extends it. In a further specialization of TextCell, a further binding of draw would not override the original declaration in Cell, nor would it override the binding in TextCell; it would extend them.

This allows for multiple levels of renement and is particularly useful as it allows an extension made to an extended component to behave as expected. With over-riding, an extension made to an already extended component would end up losing its previous extensions unless it was explicitly programmed to utilize them, thus putting an additional responsibility on the shoulders of the extension programmer.

Put simply, with the Beta model for further bindings, by making an extension it isn't possible to easily break what already works.

The INNER construct.

This allows one to specify preciselywhen, in an extensible action component, control should be transferred to the specialization. It models the fact that the extensible component decides when the extension should be invoked;

not the other way around. Extensions don't need to be concerned with specifying when they should be invoked.

Nested Specialization.

In other words, the ability to build a new component from an existing one by specializing some nested component. For example, one could specialize the TextCell component by creating a new spreadsheet system as a specialization of the old:

Listing 3

.

NewSpreadSheetSystem: SpreadSheetSystem (* create new system *)

(# (* identical to old, except: *)

TextCell::< (* text cells are slightly different *)

(# (* in their edit command *)

edit::< (# do ... issue warning; INNER #);

#)

#)

A new spreadsheet system has been constructed, reusing the old, and specializing

TextCell to issue a warning whenever edited. This is equivalent to constructing a new piece of hardware by copying most of the old and replacing some of the old parts by new extended versions. This is an elegant, powerful, and conceptually clear way to specify an extension to a system.