previous page: L18) How do the scope rules of BETA actually work?
page up: BETA Programming Language FAQ
next page: L20) Are identifiers and keyworks case-sensitive in BETA?

L19) What is a pattern?


This article is from the FAQ, by with numerous contributions by others.

L19) What is a pattern?

The following is an attempt to explain the pattern concept. The description
is divided into two parts: a description in the form of examples, and a more
abstract explanation.

To begin with, think of a pattern as a generic word for the concepts class,
procedure and function. This is not all there is to it, but it will get you
started. In BETA, a pattern is anything starting with (# and ending with #).
As a simple example, here is a function that multiplies two numbers:

   multiply: (# a,b: @integer;
             enter (a,b)
             exit a*b

The multiply pattern takes two integers, a and b, and returns their product.
These kinds of patterns are often called functional patterns, since their
use correspond to functions (or procedures) in other languages. In BETA, a
call to multiply might look like:


putInt is a procedure that writes the result on the screen.

As another example, let's build a stack class in the typical object-oriented

   stack: (# content: [100] @integer;
             currentSize: @integer;
             push: (# e: @integer;
                   enter e
                   do currentSize+1->currentSize;
             empty: (# exit (currentSize=0) #);
             pop: (# e: @integer;
                  do content[currentSize]->e;
                  exit e

Now, stack is also just a pattern. You may call it a class pattern since its
meant to be used as a class: to make instances, the actual stacks. And just
in case you were wondering: Yes, the push, empty, and pop methods defined
for stack are also patterns (functional/procedural patterns), defined inside
the stack pattern.

BETA offers a lot of extra functionality which could make the example much
more realistic (information hiding, generic stacks, exceptions due to
overflow, dynamic expansion of the stack capacity, etc.), but let's keep the
example simple.

Having shown a few practical examples, here's the more elaborate

A pattern is used for instantiating objects (static objects, dynamic
objects, inserted objects, etc.). A pattern declaration in its full form
looks like this (other forms below):

   P: S(# decl1; decl2; ... decln;
       enter enterlist
       do imperatives
       exit exitlist

This declares P as a (direct) subpattern of S. (S thus is the (direct)
superpattern of P.) S may be omitted, in which case object (the direct or
indirect superpattern of all patterns) is assumed. Each part (declarations,
enter part, do part and exit part) can be omitted. (Thus "P: (# #);" is

Each declaration can be a declaration of a pattern, a virtual pattern, a
further or final binding of a previously declared virtual pattern, a static
item, a dynamic item, a static component, a dynamic component, a repetition
(array) or a pattern variable (used for holding a pattern, popularly
speaking). Or a number of same.

Thus the above declaration of P fits into an outer pattern. If both P and S
have declared enter parts, the enter list of a P object consists of the
enter lists concatenated. The same goes for the exit list. Thus the
subpattern declaration can only add to the enter and exit lists, not make
other changes. A bit more complicated rules exist for do-parts, but the
basic principle is the same: only additions are possible.

A pattern can be virtual. There are three forms of virtual pattern

   P: (# V:< Q; #);
   P: (# V:< Q(# ... #); #);
   P: (# V:< (# ... #); #);

where Q is a pattern name.

(For the sake of completeness, this should perhaps be written as

   P: S(# ...; V:< Q; ...; enter ... do ... exit ... #);
   P: S(# ...; V:< R(# ...; enter ... do ... exit ... #); ...;
       enter ... do ... exit ...

etc., but we'll leave the that out.)

Virtual declarations can be extended, or further bound, in subpatterns:

   P1: P(# V::< Q1; #);
   P1: P(# V::< Q1(# ... #); #);
   P1: P(# V::< (# ... #); #);

In the first two forms, it is required that one of the following two
conditions holds:

1. V was declared in P (or some superpattern of P) as V:< Q, and Q1 is a
direct or indirect subpattern of Q.
2. V was already further bound in P (or some superpattern of P) using the
form V::< Q0, and Q1 is a direct or indirect subpattern of Q0.

The third form V::< (# ... #) can be used regardless of which form was used
for the previous declaration or further binding of V. In this case, the
descriptor (# ... #) is used to automatically form a subpattern. (This form
of further binding is the only available one if V is declared using V:< (#
... #), or V has been further bound using V::< Q1(# ... #) or V::< (# ...

Thus, the further binding makes V a subpattern of what it was before.

Finally, a virtual pattern may be final bound. A final binding is a further
binding, except (syntactically) :: is used instead of ::<, and
(semantically) after a final binding, V is no longer virtual, and can
therefore not be further bound. The final binding must otherwise follow the
same rules as described above for further bindings.

Also, see Question L10.


Continue to:

previous page: L18) How do the scope rules of BETA actually work?
page up: BETA Programming Language FAQ
next page: L20) Are identifiers and keyworks case-sensitive in BETA?