Attribute Accessor Routines

Sather - A Language Tutorial
Prev | Chapter 2. Classes and Objects | Next

# 2.6. Attribute Accessor Routines

The distinction between data and behavior is not as strong as has been described above. In fact, it is possible to implement a feature such that outside the class it is impossible to tell whether it is a feature or a pair of functions This section describes how this level of uniformity is achieved.

Each attribute definition adds a field to the object's state and causes the definition of a reader routine and a writer routine, both with the same name. The reader routine takes no arguments and returns the value of the attribute. Its return type is the attribute's type. The reader routine is private if the attribute is declared '`private`'. The writer routine sets the value of the attribute, taking a single argument whose type is the attribute's type, and has no return value. The writer routine is private if the attribute is declared either `private` or `readonly`.

    class INTERVAL is
       attr start:FLT;
       -- Defines the public reader start:FLT
       -- and the public writer     start(FLT)
       attr finish; INT;

       create(st,fin:INT):SAME is
          -- Create a new interval
          res:SAME := new;
          res.start(st);
          -- Equivalent to res.start := st;
          res.finish(fin);
          -- Equivalent to res.finish := fin;
       end;
    end;

Thus, the levels of privacy are defined by whether the reader and writer routines are public or private

    private attr a:FLT;   -- Defines the reader, private a:FLT
                          -- and the writer  private a(FLT);
    readonly attr b:FLT;  -- Defines the public reader, b:FLT
                          -- and the private writer b(FLT)

The same holds true for shared attribubtes. Each shared definition causes the definition of a reader routine and a writer routine, both with the same name. The reader routine takes no arguments and returns the value of the shared. Its return type is the shared's type.

    class FOO is
       shared a:INT := 3;     -- Defines a:INT and a(arg:INT);
       readonly shared b:INT; -- Defines a:INT and private a(arg:INT);
    ...

    #OUT + FOO::a;    -- Prints out 3
    FOO::a(4);        -- 'a' is set to 4, same as FOO::a := 4;
    #OUT + FOO::a;    -- Prints out 4;
    FOO::a := 7;      -- 'a' is set to '7' , equivalent to FOO::a(7);
    FOO::b(3);        -- ILLEGAL! The writer routine is private

Constants do not define a writer routine. Each constant definition causes the implicit definition of a reader routine with the same name. It takes no arguments and returns the value of the constant. Its return type is the constant's type. The routine is private if and only if the constant is declared '`private`'.

    const r:FLT:=45.6;
       -- Reader routine is r:FLT;
    private const a,b,c;
       -- Reader routine is private a:INT;
    private const d:=4,e,f
    const bar:BOOL := r > 10;
       -- Function call on constants

## 2.6.1. Attribute assignment

In order to achieve the unification of attribute assignment and routine calls, for attributes, assignment has to be given a meaning in terms of function calls.

By default, the assignment is syntactic sugar for a call of the routine with the same name as the attribute with the right hand side of the assignment as the only argument

    p:POINT := #POINT(3,5);
    p.x := 3; -- Is syntactic sugar for  p.x(3);

In the above example, the assignment to 'x' is the same as calling the routine 'x' with a single argument.

## Replacing an attribute by a routine

The beauty of this treatment of assignment is that an attribute in a class can later be substituted by a pair of routines. Consider a class to represent integer intervals, where we store the first and last value in the interval

    class I_INTERVAL is
       -- Integer intervals
       attr start:INT;     -- Defines start:INT and start(INT)
       attr finish:INT;    -- Defines finish:INT and finish(INT)

       create(start,finish:INT):SAME is
          res:SAME := new;
          res.start := start;       -- Equivalent to res.start(start);
          res.finish := finish;     -- Equivalent to res.finish(finish);
          return res;
       end;

       size:INT is return finish - start + 1; end;
            -- Returns the number of integers in the interval
    end;

We can make calls on this class

    i:I_INTERVAL := #I_INTERVAL(3,10);
    i.finish := 11;
    -- Equivalent to a call i.finish(11);
    #OUT + i.finish;
    -- Prints out 11
    i.start := 15;
    -- Equivalent to the call i.start(15);

Suppose we then realize that we usually want to know the size of the interval, and rarely need to know the end point. It would then be cheaper to store the size directly, rather than computing it. The class can be changed so that we store the `first` and `size` and compute `finish`.

    class I_INTERVAL is
       -- Integer intervals
       attr start:INT;          -- Defines start:INT and start(INT)
       readonly attr size:INT;  -- Defines  size:INT and private size(INT)
       -- size is readonly, since we only need size:INT in the interface

       create(start,finish:INT):SAME is
          res:SAME := new;
          res.start := start;          -- Equivalent to res.start(start);
          res.size :=  finish-start+1; -- Store the result in res.size
          return res;
       end;

       finish:INT is return start+size-1 end;
          -- Replacement for the reader routine for 'finish'
          -- Compute finish using 'start' and 'size'

       finish(new_finish:INT) is  size:=new_finish-start+1  end;
          -- Replacement for the writer routine for 'finish'

    end;

All the calls described above will continue to work as before. The assignment to `finish` in particular will now be a call on the user-defined finish routine, instead of a call to the implicit writer routine for the attribute `finish`.

Prev | Home | Next
Conditional Execution | Up | Static Type Inference