Tutorial: Transparency and Coercion (cont'd)


 

Some non-object contexts

There are cases where the category of entity we want is not an object. In these cases, various syntactic "tails" are available to tell the language processing system that we want e.g. a pattern. Note that there is still transparency of the declared category of entity, since we are specifying what we want, not how we get it or what we have.

Object reference wanted!

When we want to obtain an object reference from a given entity, we must annotate the entity with the "box" suffix:

[]

For example, when making an object reference refer to another object, we need to access an entity as an object reference both to obtain the future state of the target and to assign to the target as an object reference:

(#
   i: @integer;
   ir: ^integer
do
   i[]->ir[]
#)

After executing the reference assignment i[]->ir[], ir refers to the object which i (invariably) denotes.

Not executing

Note that the request for an object reference does not imply that the object should be executed. Consequently, the "[]" box marker can be used to avoid the execution of an object when that is required.

For example, assume that "p[]" is used as an imperative, i.e. not occurring in an enter- or exit-list, and not before or after an "->" evaluation arrow. Moreover, assume that p denotes a pattern. In that case it means: create an instance of the pattern p and obtain and then ignore a reference to it. This might e.g. have desirable side-effects in a concrete situation. Even more useful is the situation where or denotes an object reference attribute and and the "new" operator "&" is added, "&or[]". This means: create an instance of the declared type of or and make or refer to it.

Pattern reference wanted!

It is possible to compare the types of entities directly, and to do this we must be able to obtain a pattern reference from any entity, e.g. an object or an object reference. We also need to obtain a pattern reference in order to assign a new value to a pattern reference attribute. For these purposes we use the suffix:

##

As an example:

(#
   p: (# #);
   or: ^object;
   pr: ^p;
   var_p: ##p;
do
   (if ro## <= p## then ro[]->rp[] else NONE->rp[] if);
   p## -> var_p##
#)

The first imperative is an example of type-casing. Even if frowned upon, the notion of type-casing keeps emerging as something object-oriented programmers cannot do entirely without. The if-imperative compares the type of ro (it is the pattern from which the actual, run-time object denoted by ro were originally instantiated) with p as a pattern (implying the "empty" coercion), and iff ro is actually an instance of a specialization of p, the body ro[]->rp[] will be executed, otherwise NONE->rp[] is executed, making rp void. Afterwards, rp refers to the same object as referred by ro iff that is a type-correct thing to do.

The second imperative, p##->var_p##, is a pattern reference assignment which gives var_p the pattern p as value.

A super-pattern is a pattern context, too

Specialization is expressed in its most basic form by a super-pattern and a main part. The super-pattern is the basis on which a new pattern is created, and the main part specifies the differences and additions. Whatever occurs in the position of a super-pattern will be coerced into a pattern. The following example contains a named and an anonymous pattern created with the aid of a super-pattern:

(#
   p: q(# .. #);
do
   boolean(# .. #)
#)

Note that the expression boolean(# .. #) contains super-pattern syntax, boolean, and is itself used as an imperative. As a consequence, there is both the coercion (in this case a no-op) from pattern to pattern for boolean, and the coercion from pattern to object (instantiation), followed by execution, for the syntax boolean(# .. #).

Example 2

Here is an example which is ever so slightly less artificial (one does rather easily get tired of x-y-z-p-q examples, doesn't one? ;-). The idea is that you start an interactive gbeta session on it, single-step through some of the code and think about the coercions involved in the execution as it happens. You can always check out the category of XXX using "print XXX", usually abbreviated as in e.g. "p thyself.init".

The example looks like this:


(* FILE ex2.gb *)
-- betaenv:descriptor --
(# 
   putline: (# enter stdio do '\n'->stdio #);
   person: 
     (# init: (# enter (name,height) #);
        name: @string;
        height: @real;
        loves: (* we _love_ small persons *)
          (# other: ^person;
             value: @boolean
          enter other[] 
          do (other.height<=height)->value
          exit value 
          #);
        thumpOnHead: (# do height-0.1 -> height; 'Ouch!!'->putline #);
        greet:
          (# other: ^person
          enter other[]
          do 'Hi, '+other.name+'!'->putline
          #)
     #);
   thyNeighbor,thySelf: @person;
do 
   ('me!',6.5)->thySelf.init;
   ('pal',6.7)->thyNeighbor.init;
   
   L: (if (thyNeighbor[]->thySelf.loves)=(thySelf[]->thySelf.loves) then 
          thyNeighbor[]->thySelf.greet
       else
          thyNeighbor.thumpOnHead;
          restart L
      if)
#)

Next, we'll present some more details about evaluations.

 


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