Tutorial: The Concept of a MainPart


 

How it looks

A main part is a piece of syntax, so you can just look it up in the grammar (the file gbeta-meta.gram in the distribution). Roughly, it looks like this:

(# <attributes>
enter <evaluation>
do <imperatives>
exit <evaluation>
#)

The <attributes> section is a list of declarations. The enter-part, enter <evaluation>, specifies input properties, i.e. arguments accepted for assignment or procedure call usage. The do-part, do <imperatives>, is a list of imperatives (in many languages imperatives are called "statements") which defines the behavior associated with this main part. Finally, the exit-part, exit <evaluation>, specifies the output properties, i.e. values delivered when evaluating the main part.

All the elements are optional, so the list of declarations may be empty, and any selection of the enter-, do-, and exit-parts may be absent. This means that the main part is both a light-weight construct when only few of the basic elements are present, and it is a very rich construct when everything is there.

Main parts are the building bricks used to construct substance in gbeta programs. Either something is predefined (like the basic pattern integer), or it was constructed using a number of main parts. Like a recursive set, there are atomic elements, the predefined patterns, and composite elements, built from "smaller" elements using main parts.

Related syntax in other languages

Main parts are related to declaration blocks in other languages, such as e.g. the brace-enclosed blocks used to derive a new class from an existing one in C++,

class D : public B { .. };

or the similar Java syntax, or the keyword-enclosed feature block used in Eiffel. They are also related to behavior specification blocks, such as the brace-enclosed function bodies of a C or C++ or Java program

int my_func(float *arg) { .. }

or the keyword-based Eiffel equivalent. Finally, they include the specification of input/output properties which are more often specified outside the block in other languages, for instance in a parenthesized argument list.

Main parts can do it all!

Main parts are very versatile, supporting both the description of substance (attributes) and behavior (do-part), and specifying input/output properties (enter-/exit-parts), and this allows them to support the unification of classes, methods, co-routines, control structures, exceptions, and a lot more. Since input/output properties are specified inside the main part, there is no need to declare a name for a main part. With more traditional syntax, the grammar wouldn't easily allow the declaration of a nameless function, for example. This enables gbeta to support anonymous entities of many kinds, and they make a lot of things simpler, more convenient, and more concise.

The trade-off is that it is a matter of convention how a given main part is used in a program. It may be constructed in such a way that usage as a method is the only reasonable one, and it may be designed specifically to support a class-like usage. In practice, however, it is rarely a problem to understand the intended use, and often the generality allows a natural and valuable usage which was nevertheless unforeseen.

Example 1

The first example is a program which is similar to the hello2.gb program introduced in the "Getting Started" section:

(* FILE ex1.gb *)
-- betaenv:descriptor --
(# 
   hello: (# exit 'Hello' #);
   print: (# enter stdio #)
do 
   hello+', world!\n'->print
#)

Until you start looking into the module system, you may consider the

-- betaenv:descriptor --

to be "standard magic" which is necessary to make gbeta accept the program..

Declarations are associated with a colon and possibly some other characters, with the declared names to the left of the colon, and the value or type contraint to the right.

Declaration: <names> ":" .. <valueOrConstraint>

A gbeta program is ultimately a main part, the outermost block, and this block encloses everything in the program except for the few predefined entities. That is the main part which constitutes the whole program. Inside this main part, the patterns hello and print are declared as attributes, and there is a do-part which uses these attributes.

The pattern hello uses only the exit-part, and this is used for delivering values. When the value is a constant, the whole pattern may be considered a constant function, or simply "a constant." If this exit-part had contained a complex evaluation, much activity could have been the result of evaluating hello, such as it happens in the plus-expression in the do-part.

The pattern print uses only the enter-part, i.e. it has input properties but no attributes, behaviour, nor output properties. The input properties are directly taken over from stdio, which is a predefined entity that enables gbeta programs to read from the standard input and write to the standard output. Please note that stdio is not an example of careful language design, it was simply a quick, easy solution to the problem that gbeta had to be able to communicate with the rest of the world. Anyway, putting a value into print is exactly the same as putting a value into stdio, and that means printing it on the standard output.

Finally, the imperative in the do-part,

hello + ', world!\n' -> print

specifies the behavior that the operands of the addition are evaluated, the resulting value is computed, and this value is impressed upon print. As mentioned in the "Getting Started" section, the arrow:

->

is associated with flow of values, and the values flow in the direction of the arrow. This means that it is used to express assignment, argument delivery in method/procedure/function calls, and return value delivery from method/function calls and evaluation of the state of objects.

Think of it as an instruction to "extract a value" from whatever is on the left hand side of the arrow, and an instruction to impress a value upon whatever is on the right hand side of the arrow. The value may be arbitrarily complex, expressed by nested, parenthesized lists, and the recursive "take-over" semantics which ensures that impressing a value upon print and upon stdio is the same thing.

Next, we'll look at different kinds of declarations.

 


Signed by: eernst@cs.auc.dk. Last Modified: 3-Jul-01