Classes and Objects: Difference between revisions

From m204wiki
Jump to navigation Jump to search
m (FIx up formatting messups)
 
(99 intermediate revisions by 7 users not shown)
Line 1: Line 1:
==Classes==
==Classes==
A class is a collection of attributes that describe an object and of
A class is a collection of attributes that describe an object and of
actions that may be performed on an object.
operations that may be performed on an object.
The attributes of an object are formally declared as variables,
The attributes of an object are formally declared as [[Classes and Objects#Variable declarations|class variables]], while the operations are declared as methods.
while the actions are declared as methods.
In <var class="product">[[SOUL]]</var>, there are four types of methods: subroutines,
In the [[Janus SOAP User Language Interface]], there are four types of methods: subroutines,
functions, properties, and constructors.
functions, properties, and constructors.
While a class describes general attributes and methods, a specific
While a class ''describes'' general attributes and methods, a specific
instance of the class is called an ''object'', and variables that refer to those instances are called [[Object variables|object variables]].
''instance'' of the class is called an '''object''', and variables that refer to those instances are called [[Object variables|object variables]].
   
   
The description of a class, all variables, and all methods of the class
The description of a class, all variables, and all methods of the class
must be contained inside one or more ''class blocks'':
must be contained inside one or more <var>Class</var> blocks:
    begin
<p class="code">begin
      ...
  ...
    class car
class car
      ...
  ...
    end class
end class
      ...
  ...
    class car
class car
      ...
  ...
    end class car
end class car
      ...
  ...
    end
end
This is slightly different from most other object-oriented languages, which
</p>
require an entire class definition to be within a single class
This is slightly different from most other object-oriented languages, which require an entire class definition to be within a single class block (though sometimes a keyword other than <var>Class</var> is used to mark the start of the class definition).
block (though sometimes a keyword other than ''Class'' is used to mark the start of the class definition).
The main reason for allowing multiple class blocks is that they let you selectively include methods in a class as they are needed in a request, rather than having to compile all the methods in a class if any one is needed.
The main reason for allowing multiple class blocks is that they let you
selectively include methods in a class as they are needed in a request,
rather than having to compile all the methods in a class if any one is
needed.
   
   
For example, Subroutine Paint for Class Car could be kept in a procedure
For example, <code>Subroutine Paint</code> for <code>Class Car</code> could be kept in a procedure
as follows:
as follows:
    PROCEDURE CLASS.CAR.PAINT
<p class="code">PROCEDURE CLASS.CAR.PAINT
    !dupexit
!dupexit
    class car
class car
      subroutine paint(%color is string len 32)
  subroutine paint(%color is string len 32)
          ...
    ...
      end subroutine
  end subroutine
    end class
end class
    END PROCEDURE
END PROCEDURE
''!dupexit'' causes the procedure to be
</p>
exited on all but the first ''Include'', which ensures that the subroutine
<code>!dupexit</code> causes the procedure to be exited on all but the first <var>Include</var>, which ensures that the subroutine is only defined once.
is only defined once.
Attempting to define it more than once would result in compilation errors, of course.
Attempting to define it more than once would result in
compilation errors, of course.
   
   
Given the above procedure, code that needs to use Subroutine Paint in
Given the above procedure, code that needs to use <code>Subroutine Paint</code> in class <code>Car</code> could simply <var>Include</var> procedure <code>CLASS.CAR.PAINT</code>:
Class Car could simply Include procedure CLASS.CAR.PAINT:
<p class="code">include class.car.paint
    include class.car.paint
...
      ...
%chevy    is object car
    %chevy    is object car
...
      ...
%chevy:paint('Teal')
    %chevy:paint('Teal')
</p>
Subroutine Paint can only be defined '''after''' it has been declared inside a [[#Declaration blocks|declaration block]] for class Car. If a class is broken up in such a way that some methods are in their own procedures, it is likely that the class's declaration blocks would all be contained in another procedure, called something like ''class.car'' in this example. And that procedure would have to be ''Included''&#39;ed '''before''' the procedure that defines the Paint method:
Subroutine <code>Paint</code> can only be defined '''after''' it has been declared inside a [[#Declaration blocks|declaration block]] for class <code>Car</code>. If a class is broken up in such a way that some methods are in their own procedures, it is likely that the class's declaration blocks would all be contained in another procedure, called something like <code>CLASS.CAR</code> in this example. It would be necessary to <var>Include</var> that procedure '''before''' the procedure that defines the <code>Paint</code> method:
    include class.car
<p class="code">include class.car
    include class.car.paint
include class.car.paint
      ...
...
    %chevy    is object car
%chevy    is object car
      ...
...
    %chevy:paint('Teal')
%chevy:paint('Teal')
</p>
 
===Class block syntax===
===Class block syntax===
The full syntax of the Class block is:
The full syntax of the Class block is:
  [Local] Class <className> [[Abstract]] -
<p class="syntax"><span class="squareb">[</span>Local<span class="squareb">]</span> Class <span class="term">className</span> <span class="squareb">[</span>Abstract<span class="squareb">]</span>            -
                            [[Alias (Class and Structure)|[Alias=<aliasList>]]] -
                        <span class="squareb">[</span>Alias=<span class="term">aliasList</span><span class="squareb">]</span>      -
                            [Extends=<extendList>] -
                        <span class="squareb">[</span>Exception<span class="squareb">]</span>            -
                            [[RequiredSuffix (Class and Structure)|[RequiredSuffix=<suffix>]]]
                        <span class="squareb">[</span>Extends=<span class="term">extendList</span><span class="squareb">]</span>  -
    [class definition]
                        <span class="squareb">[</span>Private<span class="squareb">]</span>              -
  End Class [className]
                        <span class="squareb">[</span>RequiredSuffix=<span class="term">suffix</span><span class="squareb">]</span>
                        <span class="squareb">[</span><span class="term">declaration blocks</span><span class="squareb">]</span>
                        <span class="squareb">[</span><span class="term">method definitions</span><span class="squareb">]</span>
        End Class <span class="squareb">[</span><span class="term">className</span><span class="squareb">]</span>
</p>


;Local : Indicates that the class can only be referenced inside the containing [[scope]]. A class declared inside a method or complex subroutine '''must''' be ''local''. A class in the outermost (Begin/End) scope can be either ''local'', in which case it cannot be referenced in other scopes such as methods or complex subroutines, or not, in which case it can be referenced anywhere in the request.   
===Syntax terms===
;Abstract : indicates that the class is ''abstract'', that is, no instance of the class can be created unless it is an instance of a non-abstract extension class.
<table class="syntaxTable">
;<className> : can consist of any valid alphanumeric and other non-separator characters.
<tr><th><var>Local</var></th>
;<aliasList> : is a list of aliases for the class, with each alias separated by the word ''And''. Class aliases are only available in Sirius Mods 7.8 and later.
<td>Indicates that the class can only be referenced inside the containing [[scope]]. A class declared inside a method or complex subroutine ''must'' be <var>Local</var>. A class in the outermost (<var>Begin</var>/<var>End</var>) scope can be either '''local''', in which case it cannot be referenced in other scopes such as methods or complex subroutines, or not local, in which case it can be referenced anywhere in the request.</td></tr>  
;<extendList> : is a list of classes that this class extends
 
;<suffix> : is a non-quoted string of characters that must end the name of any object variable being declared for the class.
<tr><th>className</th>
<td>Consists of any valid alphanumeric and other non-separator characters.
 
<tr><th><var>Abstract</var></th>
<td>Indicates that the class is '''abstract''', that is, no instance of the class can be created unless it is an instance of a non-abstract extension class.</td></tr>
 
<tr id="aliasList"><th><var>Alias=</var>aliasList</th>
<td>A list of aliases for the class, with each alias separated by the word <var>And</var>. See [[#Aliases for class names|Aliases for class names]].</td></tr>
 
<tr><th><var>Exception</var></th>
<td>Indicates that the class is an '''exception''' class, that is an instance it can be [[Exceptions#Throwing exceptions|thrown]].</td></tr>
 
<tr><th><var>Extends=</var>extendList</th>
<td>A list of classes that this class extends.</td></tr>
 
<tr><th><var>Private</var></th>
<td>Indicates that the class is a '''private''' class, meaning that only the class itself and friends can reference class members. <var>Private</var> is only available in Model&nbsp;204 7.6 and later. See [[#Friend declarations|Friend declarations]].</td></tr>
 
<tr><th><var>RequiredSuffix=</var>suffix</th>
<td>A non-quoted string of characters that must end the name of any object variable being declared for the class; provides a way of enforcing naming standards for class variable names. </td></tr>
 
<tr><th>declaration blocks</th>
<td>Declarations for all variables and methods in a class. See [[#Declaration blocks|Declaration blocks]]. </td></tr>
 
<tr><th>method definitions</th>
<td>The definitions of the methods in the class. See [[Methods#Method definition syntax|Method definition syntax]]. </td></tr>
</table>
 
The (one or more) class blocks for a class contain the declaration blocks and method definitions for the class, which together comprise the '''class definition'''.
The class definition consists of zero or more '''declaration blocks''' and zero or more '''method definitions'''.
It is valid to define a class with no declarations and no methods, though such a class is not going to be particularly useful.
 
Multiple class blocks for a class are, in effect, combined to form one class definition. Class blocks after the first may exactly repeat any of the <var>Alias</var>, <var>Extends</var>, or <var>RequiredSuffix</var> clauses from the first block for a class, but they may not change these clauses.
   
   
The (one or more) class blocks for a class contain the
Classes must be declared outside of any other block structures such as <var>For</var> loops, <var>If</var> blocks, or other <var>Class</var> blocks.
''class definition''.
The class definition consists of zero or more ''declaration blocks'' and
zero or more ''method definitions''.
It is valid to define a class with no declarations and no
methods, though such a class is not going to be particularly useful.
   
   
Classes must be declared outside of any other block structures such as For loops, If blocks, or other Class blocks.
Once a class is declared, you use the <var>Object</var> keyword on variable declarations to declare objects as instances of a class:
<p class="code">%malibu  is object car
Once a class is declared,
%outback  is object car
you use the Object keyword on variable declarations to
</p>
declare objects as instances of a class:
 
    %malibu  is object car
    %outback  is object car
===Colons in class names===
===Colons in class names===
The colon character (:) is allowed in User Language class names.
The colon character (<tt>:</tt>) is allowed in <var class="product">SOUL</var> class names.
The following, for example, is a valid class declaration:
The following, for example, is a valid class declaration:
    class customer:order
<p class="code">class customer:order
</p>
No colon-demarcated part of a class name can be the word <var>System</var>.
For example, <code>System</code>, <code>System:foo</code>, <code>Foo:system</code>, and <code>Foo:system:bar</code> are all '''''invalid''''' class names.
   
   
No colon-demarcated part of a class name can be the word ''System''.
While there is no special meaning to the colon-demarcated parts of a class name, the intent is that the colons be used to group classes, and that higher-level qualifiers come before lower-level ones.
For example, ''System'', ''System:foo'', ''Foo:system'',
For example, in class name <code>MiddleEarth:rohan:rider</code>, a <code>Rider</code> class falls under a grouping of classes qualified by <code>Rohan</code>.
and ''Foo:system:bar'' are all ''invalid'' class names.
This grouping, itself, falls under the larger grouping of <code>MiddleEarth</code>.
Using the colon in this way is likely to make it easier to take advantage of future class management features that depend on a hierarchical, colon-based, class naming scheme.
 
===Aliases for class names===
As of <var class="product">Sirius Mods</var> Version 7.8, you can define one or more alias names for an existing user class.
You do so by specifying an <var>[[#aliasList|Alias]]</var> list in the class declaration. For example,
you are changing a naming convention for some code, and you want to refer to user-defined class <code>Blue</code> by another name, say <code>Indigo</code>. Your class declaration would be:
<p class="code">class blue alias indigo
</p>
You can then use either the primary class name or the alias when declaring objects of the class:
<p class="code">%foo    is object blue
%bar     is object indigo
</p>
An object of the alias class is compiled as an object
of the primary class, and the runtime class of an
object variable that was defined using an alias is the primary class.
Consequently, all system messages you receive will specify the primary class.
   
   
While there is no special meaning to the colon-demarcated parts of a class
For example, given the declarations above, if you call method <code>TouchUp</code> for an object declared for the Indigo class:
name, the intent is that the colons be used to group classes, and that
<p class="code">%bar:touchUp
higher-level qualifiers come before lower-level ones.
</p>
For example, in class name ''MiddleEarth:rohan:rider'', a
And method <code>TouchUp</code> does not exist in the class, the error message you receive is:
Rider class falls under a grouping of classes qualified by Rohan.
<p class="code">MSIR.0733: Member TOUCHUP not found in class Blue
This grouping, itself, falls under the larger grouping of MiddleEarth.
</p>
Using the colon in this way is likely to make it easier to take advantage
While this might seem potentially confusing, aliases are intended primarily for migrating class names, so any confusion will be limited to this migration period. In addition, only the owner of a class can declare an alias, so aliases are not likely to proliferate in your site's code.
of future class management features that depend on a
 
hierarchical, colon-based, class naming scheme.
Multiple aliases in the alias list are separated by the keyword <var>And</var>.<!--SOUL.DME.278.QA has an example of multiple aliases, probably don't need to belabor it here in the doc-->
 
==Declaration blocks==
==Declaration blocks==
''Declaration blocks'' are another feature of the Janus SOAP User Language Interface that is unusual
'''Declaration blocks''' are another feature of <var class="product">SOUL</var> that is unusual
among object-oriented languages.
among object-oriented languages.
Declaration blocks describe all the variables and methods that make
Declaration blocks describe all the variables and methods that make up a class, but they contain no code.
up a class, but they contain no code.
   
   
Most object-oriented languages allow variable declarations of all types to be
Most object-oriented languages allow variable declarations of all types to be mixed arbitrarily with method definitions of all types.
mixed arbitrarily with method definitions of all types.
Though this appears to be very convenient, it can make it difficult for users or maintainers of a class to determine the general framework of the class.
Though this appears to be
<var class="product">SOUL</var> requires all variables and methods of a class to be declared in a few blocks, and in those blocks, to be organized in ways that are significant to their behavior.
very convenient, it can make it difficult
for users or maintainers of a class to determine the general framework
of the class.
The Janus SOAP User Language Interface requires all variables and methods of a class to be declared
in a few blocks, and in those blocks, to be organized in ways that are
significant to their behavior.
   
   
The Janus SOAP User Language Interface allows no more than one of each of four declaration blocks:
<var class="product">SOUL</var> allows no more than one of each of four declaration blocks:
*Public
<ul>
*Private
<li><var>Public</var>
*Public Shared
<li><var>Private</var>
*Private Shared
<li><var>Public Shared</var>
<li><var>Private Shared</var>
</ul>
The <var>Public</var> block contains the declaration of all class ''members'' (variables that are
associated with an instance of the class, and methods that operate on an instance of the class) that are "public", that is available to users of the class.
The <var>Private</var> block contains the declaration of all members that are "private", that is available only to methods inside a class definition.
   
   
The Public block contains the declaration of all class ''members''
The Shared blocks contain members that are shared among '''all''' instances of the class and so are able to be referenced independent of any instance of the class.
(variables that are
<var>Public Shared</var> members are available to all users of the class, and <var>Private Shared</var> members are available only within the class definition.
associated with an instance of the class, and methods that operate on
<var>Shared</var> variables and methods are discussed in more detail in [[Shared class members]].
an instance of the class) that are &ldquo;public,&rdquo; that
is, available to users of the class.
The Private block contains the declaration of all members that are
&ldquo;private,&rdquo; that is, available only to methods inside a class definition.
   
   
The Shared blocks contain members that are shared among '''all'''
The declaration blocks contain declarations for all variables and methods in a class, and they must appear before any method definitions.
instances of the class and so are able to be referenced independent of any
 
instance of the class.
===Declaration block syntax===
Public Shared members are available to all users of the class, and
Private Shared members are available only within the class definition.
Shared variables and methods are discusssed in more detail in
[[Shared class members]].
The declaration blocks contain declarations for all variables and
methods in a class, and they must appear before any method definitions.
The format of the declarations blocks is:
The format of the declarations blocks is:
===Declaration block syntax===
{Public | Private} [Shared]
  {Public | Private} [Shared]
    [Allow clauses]
      [[#Allow clause|[allow clauses]]]
    [Disallow clauses]
      [member declarations]
    [Friend declarations]
      [disallow clauses]
    [member declarations]
  End {Public | Private} [Shared] [className]
End {Public | Private} [Shared] [className]
Syntax notes:
 
*The ''End'' for a declaration block must exactly match the block type: for a Public block, ''End Public'' is required; for a Private Shared block, ''End Private Shared'' is required.
===Syntax terms===
*The class name can, optionally, be indicated after the block type on the block ''End''.
<table class="syntaxTable">
*The optional ''allow clauses'' must be specified in the Public (non-shared) block, and consist of the ''Allow'' keyword followed by ''Auto'', ''Copy'', ''DeepCopy'', or ''Narrow''.
<tr><th><var>Public&nbsp;|&nbsp;Private</var></th><td>Indicates whether the block describes members that can be referenced outside the class (<var>Public</var>) or members that can only be referenced inside the class (<var>Private</var>) or by '''friend''' classes.</td></tr>
*The optional ''disallow clauses'' must be specified in the Public (non-shared) block, and consist of the ''Allow'' keyword followed by ''New'' or ''Discard''.
 
*There can be multiple ''Allow'' and ''Disallow'' clauses in a declaration block.  
<tr><th>Allow clauses</th><td>Indicate special operations allowed by the class. They can only be specified in a <var>Public</var> (non-shared) block, and consist of the <var>Allow</var> keyword followed by <var>Auto</var>, <var>Copy</var>, <var>DeepCopy</var>, or <var>Narrow</var>. See [[#Allow clause|Allow clause]], below.
*Allow clauses, member declarations, and disallow clauses can generally appear in any order inside the declaration block.
<p>
*The ''member declarations'' consist of variable or method declarations. These are described in following subsections.
There can be multiple <var>Allow</var> clauses in a declaration block. </p>
*While ''member declarations'' are optional, they tend to appear in almost all declarations blocks and a class without member declarations in '''some'' declaration blocks would be pretty much useless.
<var>Allow</var> clauses are optional and can be interspersed with <var>Disallow</var> clauses, <var>Friend</var> declarations, and member declarations.</td></tr>
 
<tr><th>Disallow clauses</th><td>Indicate operations not allowed by the class. They can only be specified in a <var>Public</var> (non-shared) block, and consist of the <var>Disallow</var> keyword followed by <var>Discard</var>, or <var>New</var>. See [[#Disallow clause|Disallow clause]], below.
<p>
There can be multiple <var>Disallow</var> clauses in a declaration block. </p>
<var>Disallow</var> clauses are optional and can be interspersed with <var>Allow</var> clauses, <var>Friend</var> declarations, and member declarations.</td></tr>
 
<tr><th>Friend declarations</th><td>Indicate '''friends''' of a class. Friends of a class are other classes that can access private members of the class or, in the case of a '''public friend''', access public members of a private class. They can only be specified in a <var>Private Shared</var> block or the <var>Public Shared</var> block of a private class. In the latter case, a friend declared in a <var>Public Shared</var> block is called a '''public friend'''.
<p>
Friend declarations consist of the <var>Friend</var> keyword followed by the name of another class. There can be multiple <var>Friend</var> declarations in a declaration block. <var>Friend</var> clauses are optional and can be interspersed with <var>Allow</var> clauses, <var>Disallow</var> clauses, and member declarations.</p>
<p class="note"><b>Note:</b> Friend classes do not have to have been declared, nor do they ever have to be declared inside a request. </p>
<p>
Friend declarations are available in Model 204 7.6 and later.</p></td></tr>
 
<tr><th nowrap>member declarations</th><td>Declare variables or methods in the class. These are described in [[#Variable declarations|Variable declarations]] and [[#Method declarations and method types|Method declarations and method types]], below.
While member declarations are optional, they tend to appear in almost all declarations blocks; and a class without member declarations in ''some'' declaration blocks would be pretty much useless.
Member declaration clauses can be interspersed with <var>Allow</var> clauses, <var>Disallow</var> clauses, and <var>Friend</var> declarations.</td></tr>
 
<tr><th><var>End</var></th><td>Marks the end of the declaration block. It must be followed immediately by <var>Public</var> or <var>Private</var> which is then possibly followed by <var>Shared</var>. The keywords following <var>End</var> must exactly match the block type: for a <var>Public</var> block, <var>End Public</var> is required; for a <var>Private Shared</var> block, <var>End Private Shared</var> is required.
The class name can, optionally, be indicated after the block type on the <var>End</var> clause.</td></tr>
</table>
 
===Allow clause===
===Allow clause===
As the name suggests, the ''Allow'' clause indicates things one is allowed to do with the object instances of the class that one ordinarily would be allowed to do:  
As the name suggests, the <var>Allow</var> clause indicates things one is allowed to do with the object instances of the class that one ordinarily would be allowed to do:
;Allow Auto : Indicates that an ''Auto'' ''New'' clause may be used on the declaration of variables of the type of the class being defined. If the class is an extension class, Allow Auto must also be specified on the class's base class(es).<br/><br/>Prior to version 7.6 of the Sirius Mods, a class defined with Allow Auto is not allowed to contain a constructor. As of version 7.6, Allow Auto is allowed in a class that has a constructor. However, for an object variable declared with ''Auto New'' in such a class, an automatic instantiation of the object does '''not''' call any of the constructors in the class, even if the class has defined an explicit constructor with the name ''New''.<br/><br/>For example, the following request prints &ldquo;%y:b = 0&rdquo; &mdash; the class's New constructor does '''not''' get called by ''%y:b'':
<table class="syntaxTable">
    b
<tr><th><var>Allow Auto</var></th>
    class y
<td>Indicates that an <var>Auto New</var> clause may be used on the declaration of variables of the type of the class being defined. If the class is an extension class, <var>Allow Auto</var> must also be specified on the class's base class(es).</td></tr>
      public
 
        allow auto
<tr><th><var>Allow&nbsp;Copy</var> or
        variable  b is float
<var>Allow&nbsp;DeepCopy</var></th>
        constructor new
<td>Makes available the system <var>Copy</var> or <var>DeepCopy</var> method, respectively, for objects in the class.</td></tr>
      end public
 
    &nbsp;
<tr><th><var>Allow Narrow</var></th>
    constructor new
<td>Permits narrowing assignments for objects in the class.</td></tr>
      %this:b = 1234
</table>
    end constructor
 
    &nbsp;
====Allow Auto and constructors====
    end class
Prior to version 7.6 of the <var class="product">Sirius Mods</var>, a class defined with <var>Allow Auto</var> is not allowed to contain a constructor. As of version 7.6, <var>Allow Auto</var> is allowed in a class that has a constructor. However, for an object variable declared with <var>Auto New</var> in such a class, an automatic instantiation of the object does '''not''' call any of the constructors in the class, even if the class has defined an explicit constructor with the name <var>New</var>.
    &nbsp;
 
    %y is object y auto new
For example, the following request prints <code>%y:b = 0</code> &mdash; the class's <code>New</code> constructor does '''not''' get called by <code>%y:b</code>:
    [[AuditText, PrintText, and TraceText Statements|printText]] {~} = {%y:b}
<p class="code">b
    end
class y
;Allow Copy or Allow DeepCopy : Makes available the system Copy or DeepCopy method, respectively, for objects in the class.
  public
;Allow Narrow : Permits narrowing assignments for objects in the class.
      allow auto
      variable  b is float
      constructor new
  end public
  &nbsp;
  constructor new
      %this:b = 1234
  end constructor
  &nbsp;
end class
&nbsp;
%y is object y auto new
[[Targeted Text statements|printText]] {~} = {%y:b}
end
</p>
 
===Disallow clause===
===Disallow clause===
As the name suggests, the ''Disallow'' clause indicates things that one is not allowed to do with the object instances of the class that ordinarily '''would'''  be allowed:  
As the name suggests, the <var>Disallow</var> clause indicates things that one is not allowed to do with the object instances of the class that ordinarily '''would'''  be allowed:
; Disallow Discard : Indicates that objects of the class canot be explicitly discarded outside the class. Objects of the class can still be explicitly ''Discard''ed inside the class.
<table class="syntaxTable">
; Disallow New : Indicates that explicit construction of instances of the class using the New constructor is not allowed outside the class. Objects of the class can still be explicitly created with ''New'' inside the class, and other public constructors for the class can be invoked outside the class.  
<tr><th><var>Disallow&nbsp;Discard</var></th><td>Indicates that objects of the class cannot be explicitly discarded outside the class. Objects of the class can still be explicitly <var>Discard</var>ed inside the class. For more about discarding objects, see [[Object variables#Discarding objects|"Discarding objects"]]</td></tr>
 
<tr><th><var>Disallow&nbsp;New</var></th><td>Indicates that explicit construction of instances of the class using the New constructor is not allowed outside the class. Objects of the class can still be explicitly created with <var>New</var> inside the class, and other public constructors for the class can be invoked outside the class. For more about object constructors, see [[Object variables#Creating object instances|"Creating object instances"]]</td></tr>
</table>
===Friend declarations===
The <var>Friend</var> clause declares another class that can access private members in the class being declared or, if inside a <var>Public Shared</var> block (Private class only), another class that can access public members in the class being declared.
The class declared as a friend need not have been declared, nor does it ever need to be declared inside a request. A class can only ever have friends in the same scope, so a <var>Friend</var> statement inside a <var>Local Class</var> declaration only applies to classes in the scope that contains the local class.
 
In the following example, class <code>Moe</code> is a friend of class <code>Curly</code> so can access private variables in class <code>Curly</code>:
<p class="code">class curly
  public
      variable weight is float
      variable nyucks is float
      variable lastName is string len 32
  end public
  private
      variable painlevel is float
  end private
  private shared
      friend moe
  end private shared
end class
 
class moe
  public
      variable salary  is float
      variable oughtas is float
      variable lastName is string len 32
      variable partner  is object curly
      subroutine smackPartner
  end public
  subroutine smackPartner
      %this:partner:painLevel = @ + 5
  end subroutine
end class
</p> 
If <code>Moe</code> had not been declared as a friend of <code>Curly</code> then <code>subroutine smackPartner</code> in class <code>Moe</code> would not have been able to reference the <code>PainLevel</code> member of class <code>Curly</code>.
 
Note that if we change the declaration of <code>Curly</code> to be <var>Private</var>, <code>Moe</code> could continue to access both public and private members of <code>Curly</code> since it is declared inside the <var>private Shared</var> block. However, in that case, a class could be declared as a friend inside a <var>Public Shared</var> block, creating a ''public friend'' that can only access public members of <code>Curly</code>. Public friends are only allowed for <var>Private</var> classes. In the following, <code>Larry</code> is a public friend of <code>Curly</code> so can reference public members:
<p class="code">class curly private
  public
      variable weight is float
      variable nyucks is float
      variable lastName is string len 32
  end public
  private
      variable painlevel is float
  end private
  private shared
      friend moe
  end private shared
  public shared
      friend larry
  end public shared
end class
 
class larry
  public
      variable hairLength is float
      variable partner  is object curly
      subroutine amusePartner
  end public
  subroutine amusePartner
      %this:partner:nyucks = @ + 2
  end subroutine
end class
</p>
 
Note that non-friends can always reference instances of private classes, they simply cannot reference class members. So the following is allowed:
<p class="code">class curly private
  public
      variable weight is float
      variable nyucks is float
      variable lastName is string len 32
  end public
  private
      variable painlevel is float
  end private
  private shared
      friend moe
  end private shared
  public shared
      friend larry
  end public shared
end class
 
%curly is object curly
%moe  is object moe
...
%curly = %moe:partner
</p>
 
===Variable declarations===
===Variable declarations===
Variable declarations in a class declaration block must begin with ''Variable''
Variable declarations in a class declaration block must begin with <var>Variable</var> followed by the variable name (without a percent sign). This is followed by any standard <var class="product">SOUL</var> variable declaration, with the exception that:
followed by the variable name (without a percent sign).
<ul>
This is followed
<li><var>Static</var> is not valid in a non-Shared block. </li>
by any standard User Language variable declaration, with the exception that:
 
*The keyword ''''Static'''' is not valid in a non-Shared block.
<li><var>Global</var> is not valid in a non-Shared block and requires the global name in a Shared block. </li>
*The keyword ''''Global'''' is not valid in a non-Shared block and requires the global name in a Shared block.
 
*The keyword ''''Common'''' is not allowed.
<li><var>Common</var> is not allowed. </li>
*The keywords to indicate &ldquo;publicity&rdquo; (''''Private'''', ''''Private Shared'''', ''''Public'''', and ''''Public Shared'''') are allowed. If specified, they must match the publicity of the enclosing declaration block.
 
*The keyword ''''Auto'''' followed by ''''New'''' or an enumeration value are allowed.
<li>The keywords to indicate "publicity" (<var>Private</var>, <var>Private Shared</var>, <var>Public</var>, and <var>Public Shared</var>) are allowed. If specified, they must match the publicity of the enclosing declaration block. </li>
*The keywords ''''ReadOnly'''' and ''''Protected'''' are allowed in Sirius Mods 7.2 and later.
 
Like standard [[Model 204]] variable declarations, the keyword ''''Is'''' immediately after the variable name on variable declarations is optional.
<li><var>Auto</var> followed by <var>New</var> or an enumeration value is allowed. </li>
 
<li>For <var>ReadOnly</var> and <var>Protected</var> variable declarations, like standard <var class="product">Model&nbsp;204</var> variable declarations, the keyword <var>Is</var> immediately after the variable name on variable declarations is optional. </li>
</ul>
 
====Variable declaration examples====
<p>
An example follows of a class block with some variable declarations:
An example follows of a class block with some variable declarations:
    class car
</p>
      public
<p class="code">class car
          variable    price      is fixed  dp 2
  public
          variable    make        is string len 32
      variable    price      is fixed  dp 2
          variable    description is longstring
      variable    make        is string len 32
      end public
      variable    description is longstring
    end class
  end public
Class variables can be referenced via an instance of that class, that is,
end class
via an ''object variable'', like ''%gremlin'' below:
</p>
    %gremlin  is object car
 
      ...
====Variable declaration references====
    print 'The price is ' %gremlin:price
Class variables can be referenced with an instance of that class, that is,
with an ''object variable,'' like <code>%gremlin</code> below:
<p class="code">%gremlin  is object car
  ...
printText The price is {%gremlin:price}
</p>
The colon (<tt>:</tt>) that separates the object variable <code>%gremlin</code> and the class member variable <code>price</code>, above, may alternatively be specified with a one or more blanks before and/or after it.
   
   
The colon (''':'') that separates the object variable ''%gremlin''
====ReadOnly and Protected variables====
and the class member Variable ''price'', above, may alternatively be
It is possible to to declare a variable as <var>ReadOnly</var> or <var>Protected</var>.
specified with a single blank before and/or after it.
A <var>ReadOnly</var> variable can be examined by code outside the class, but it can only
In Sirius Mods 7.2 and later, it is possible to to declare a variable
as ''ReadOnly'' or ''Protected''.
A ''ReadOnly'' variable can be examined by code outside the class, but it can only
be updated inside the class.
be updated inside the class.
A ''Protected'' variable can be examined by code outside the class, but it can only
A <var>Protected</var> variable can be examined by code outside the class, but it can only
be updated inside the class or by code inside an extension class.
be updated inside the class or by code inside an extension class.
Since a ReadOnly or Protected variable must be accessible outside the class,
Since a <var>ReadOnly</var> or <var>Protected</var> variable must be accessible outside the class,
it would make no sense for such a variable to be in a Private section, so this
it would make no sense for such a variable to be in a <var>Private</var> section, so this is not allowed.
is not allowed.
   
   
ReadOnly and Protected variables are useful for providing an efficient means of
<var>ReadOnly</var> and <var>Protected</var> variables are useful for providing an efficient means of accessing relatively static information about objects in a class. Unlike a property, retrieving <var>ReadOnly</var> or <var>Protected</var> variables requires no code to be run in the class.
accessing relatively static information about objects in a class.
Unlike a property, retrieving ReadOnly or Protected variables requires no code
to be run in the class.
   
   
The term ''Protected'' is something of a misnomer.
The term <var>Protected</var> is something of a misnomer. In a very real sense, <var>Protected</var> variables aren't protected, at all. After all, they can be updated by extension classes. Their real purpose is to act as a sort of overridable variable, that is, a value that can be overridden by an extension class.
In a very real sense, Protected variables aren't protected, at all.
After all, they can be updated by extension classes.
Their real purpose is to act as a sort of overridable variable, that is, a
value that can be overridden by an extension class.
   
   
For example, suppose a class has a ReadOnly variable called PartNumber.
For example, suppose a class has a <var>ReadOnly</var> variable called <code>PartNumber</code>. Perhaps <code>PartNumber</code> is set by the class's constructor, and then it is never set again (for a particular object instance).
Perhaps PartNumber is set by the class's constructor, and then it is never set
Now, suppose this class wants to allow extension classes to set a different <code>PartNumber</code>, probably in their constructors.
again (for a particular object instance).
One approach would be to make <code>PartNumber</code> an <var>Overridable</var> <var>ReadOnly</var> property.
Now, suppose this class wants to allow extension classes to set a different
But this is a somewhat heavyweight approach, as it requires a bit of code (the property <var>Get</var> method) for each extension class, and this code has to be run every time <code>PartNumber</code> is retrieved.
PartNumber, probably in their constructors.
Instead, <code>PartNumber</code> could be made a <var>Protected</var> variable, and an extension class's constructor could simply set it after calling the base class constructor.
One approach would be to make PartNumber an Overridable ReadOnly property.
This allows the extension classes to override the base class's <code>PartNumber</code> using little extra code and using a very efficient access path for the value.
But this is a somewhat heavyweight approach, as it requires a bit of code
(the property Get method) for each extension class, and this code has to be
run every time PartNumber is retrieved.
Instead, PartNumber could be made a Protected variable, and an extension class's
constructor could simply set it after calling the base class constructor.
This allows the extension classes to override the base class's PartNumber
using little extra code and using a very efficient access path for the value.
   
   
Protected variables differ from Overridable ReadOnly properties,
<var>Protected</var> variables differ from <var>Overridable ReadOnly</var> properties, however, in that an <var>Overridable ReadOnly</var> property always guarantees that the extension class's code is run, so it overrides the base class's properties.
however, in that an Overridable ReadOnly property always guarantees that the
With a <var>Protected</var> variable, it would be possible for an extension class to set it, and for the base class to then set it to something else, undoing the extension class action.
extension class's code is run, so it overrides the base class's properties.
With a Protected variable, it would be possible for an extension class to
set it, and for the base class to then set it to something else, undoing
the extension class action.
   
   
Because they can be updated by both base and extension classes,
Because they can be updated by both base and extension classes, <var>Protected</var> variables are probably most useful for very static values, like values that are only set by the constructors.
Protected variables are probably most useful for very
Use of <var>Protected</var> variables that can be set throughout the life of objects of a class is likely to be error-prone, as it requires careful coordination of updates between the [[Inheritance and polymorphism|base and extension classes]].
static values, like values that are only set by the constructors.
 
Use of Protected variables that can be set throughout the life of objects
of a class is likely to be error-prone, as it requires careful coordination
of updates between the [[Inheritance and polymorphism|base and extension classes]].
===Method declarations and method types===
===Method declarations and method types===
In addition to variable declarations, a declaration block can also
In addition to variable declarations, a declaration block can also
contain ''method declarations''.
contain ''method declarations''.
There are four basic types of methods:
There are four basic types of methods:
;Functions : Methods that produce an output value.
<table class="syntaxTable">
;Subroutines : Methods that produce no output value.
<tr><th><var>[[Methods#Function|Function]]</var></th><td>A method that produces an output value.</td></tr>
;Properties : Methods that produce an output value and that can sometimes be set to a value, that is, be on the left side of an assignment statement.
 
;Constructors : Methods that process objects immediately after the objects are instantiated. Constructors produce no output value.
<tr><th><var>[[Methods#Subroutine|Subroutine]]</var></th><td>A method that produces no output value.</td></tr>
 
The declarations for these methods are very similar:
<tr><th><var>[[Methods#Property|Property]]</var></th><td>A method that sometimes produces an output value and that can sometimes be set to a value, that is, be on the left side of an assignment statement.</td></tr>
 
<tr><th><var>[[Object variables#Using New or other Constructors|Constructor]]</var></th><td>A method that processes objects immediately after the objects are instantiated. A <var>Constructor</var> produces no output value.</td></tr>
</table>
 
====Method declaration syntax====
====Method declaration syntax====
  Function <name>                                           -
The declarations for the four types of methods are very similar:
      [(parameters)] [Is] <type> [Optional | Default <value>-
<p class="syntax">Function <span class="term">name</span>                                                 -
      [AllowNullObject] [CurrentRecord <context>] [Callable]   -
        [(<span class="term">parameters</span>)] [Is] <span class="term">datatype</span>                         -
      [Public | Private [Shared] ]
        [AllowNullObject] [CurrentRecord <span class="term">context</span>] [Callable] -
        [Public | Private [Shared] ]
  Subroutine <name>                                           -
&nbsp;
      [(parameters)] [Optional | Default <value>]             -
Subroutine <span class="term">name</span>                                               -
      [AllowNullObject] [CurrentRecord <context>]             -
          [(<span class="term">parameters</span>)]                                     -
      [Public | Private [Shared] ]
          [AllowNullObject] [CurrentRecord <span class="term">context</span>]           -
          [Public | Private [Shared] ]
  Property <name>                                             -
&nbsp;
      [(parameters)] [Is] <type> [Optional | Default <value>-
Property <span class="term">name</span>                                                 -
      [ReadWrite | ReadOnly | WriteOnly]                       -
        [(<span class="term">parameters</span>)] [Is] <span class="term">datatype</span>                         -
      [AllowNullObject] [CurrentRecord <context>]             -
        [ReadWrite | ReadOnly | WriteOnly]                   -
      [Public | Private [Shared] ]
        [AllowNullObject] [CurrentRecord <span class="term">context</span>]             -
        [Public | Private [Shared] ]
  Constructor <name>                                         -
&nbsp;
      [(parameters] [Optional | Default <value>]]              -
Constructor <span class="term">name</span>                                               -
      [Public | Private]
            [(<span class="term">parameters</span>)]                                     -
Syntax notes:
            [Public | Private]
*The ''''name'''' for Functions, Subroutines, Properties, and Constructors must follow the same rules as variable names and cannot be the same as another member of the class, whether a variable or method, public or private, shared or non-shared.
</p>
*The ''''parameters'''' have the same format as the parameter declarations of complex subroutines. For example:
 
    class car
====Syntax terms====
      public
<table class="syntaxTable">
        ...
<tr><th>name</th>
        subroutine wash(%detergent is string len 16, -
<td>The name for a <var>Function</var>, <var>Subroutine</var>, <var>Property</var>, or <var>Constructor</var> must follow the same rules as variable names and cannot be the same as another member of the class, whether a variable or method, public or private, shared or non-shared.
                        %howLong  is float,        -
</td></tr>
                        %howClean  is fixed output)
<tr><th>parameters</th>
        ...
<td>Method parameters have the same format as the parameter declarations of complex subroutines. For example:
      end public
<p class="code">class car
    end class
  public
*Functions and properties require a datatype, that is, an Is clause (though the keyword ''Is'' is optional).
    ...
*For functions, the datatype is the datatype returned by the function.
    subroutine wash(%detergent is string len 16, -
*For properties, the datatype is both the datatype returned by the property (when the property is used as an input) and the datatype to which the property is set. For example, suppose class Car had a mileage property:
                    %howLong  is float,        -
    class car
                    %howClean  is fixed output)
      public
    ...
        ...
  end public
        property mileage  is float
end class
        ...
</p></td></tr>
      end public
<tr><th><div id="datatype"></div>datatype</th>
    end class
<td>Functions and properties require a datatype, that is, an <var>Is</var> clause (though the keyword <var>Is</var> is optional).
<ul>
<li>For functions, the datatype is the datatype returned by the function.
<li>For properties, the datatype is both the datatype returned by the property (when the property is used as an input) and the datatype to which the property is set. For example, suppose class <code>Car</code> had a mileage property:
<p class="code">class car
  public
    ...
    property mileage  is float
    ...
  end public
end class</p>
You could do:
You could do:
    %civic  is object car
<p class="code">%civic  is object car
        ...
    %civic:mileage = 32.6
        ...
    %hybridMileage = %civic:mileage + 19
*The ''''Optional'''' keyword indicates that a parameter may be specified on the method invocation or not. Alternatively, ''''Default'''' lets you provide a default parameter value.
*The ''''AllowNullObject'''' keyword on method declarations indicates that, even though the method operates on an instance of the class (an object), the method may be invoked with a null object variable. Ordinarily, a non-shared method referenced by a null object variable causes a (null-object reference) request cancellation error. Invoking a method even when the object variable is null may be useful in diagnostic code that displays object status or cleanup code.<br/><br/>Since a constructor will always receive the just-created object as the object variable, AllowNullObject is ''not'' allowed on constructor declarations.
*The ''''CurrentRecord'''' clause (''CurrentRecord In File <name>'', or ''CurrentRecord In Group <name>'') on method declarations indicates that the method may only be invoked in a record context (established by a ''For Each Record'' loop, for example) for the declared file or group. Furthermore, ''CurrentRecord'' establishes a record context '''within''' the method, so method statements may reference a record field without having to be wrapped inside a record For loop (such as a For Record CurrentRecord loop).
*The ''''Callable'''' keyword, available only on Function declarations, indicates that the function can be Called, that is, invoked without a target for the result. Such a function may be Called even though it returns a value, and the Call may be explicit or implicit.
    class car
      public
          ...
          function wash is float callable
          ...
      end public
    end class
     ...
     ...
    %beetle  is object car
%civic:mileage = 32.6
     ...
     ...
    %beetle:wash
%hybridMileage = %civic:mileage + 19
</p></ul></td></tr>
 
<tr><th><var>AllowNullObject</var></th>
<td>The <var>AllowNullObject</var> keyword on a method declaration indicates that, even though the method operates on an instance of the class (an object), the method may be invoked with a null object variable. Ordinarily, a non-shared method referenced by a null object variable causes a (null-object reference) request cancellation error. Invoking a method even when the object variable is null may be useful in diagnostic code that displays object status or cleanup code.
<p>
Since a <var>Constructor</var> will always receive the just-created object as the object variable, <var>AllowNullObject</var> is ''not'' allowed on <var>Constructor</var> declarations.</p></td></tr>
 
<tr><th><var>CurrentRecord</var></th>
<td>The <var>CurrentRecord</var> clause (<code>CurrentRecord In File <name></code>, or <code>CurrentRecord In Group <name></code>) on a method declaration indicates that the method may only be invoked in a record context (established by a <var>For Each Record</var> loop, for example) for the declared file or group. Furthermore, <var>CurrentRecord</var> establishes a record context '''within''' the method, so method statements may reference a record field without having to be wrapped inside a record <var>For</var> loop (such as a <var>For Record CurrentRecord</var> loop).</td></tr>
 
<tr><th><div id="callable"></div><var>Callable</var></th>
<td>The <var>Callable</var> keyword, available only on <var>Function</var> declarations, indicates that the function can be Called, that is, invoked without a target for the result. Such a function may be Called even though it returns a value, and the <var>Call</var> may be explicit or implicit.
<p class="code">class car
  public
      ...
      function wash is float callable
      ...
  end public
end class
...
%beetle  is object car
...
%beetle:wash
</p>
The last line can also be written:
The last line can also be written:
    call %beetle:wash
<p class="code">call %beetle:wash</p>
''Callable'' is typically to be used on Functions that perform some action but also return a status or informational value. Since the caller might not care about the informational value, the Callable keyword allows the caller to invoke the Function without a target for the return value.<br/><br/>Without the Callable keyword, an attempt to invoke a Function without a target for the result (the target of an assignment or input to another method or subroutine) causes a compilation error. Not having the Callable option makes sense for Functions whose chief purpose is to return a value, since invoking them without a return value defeats the purpose. For example, if you had:
<var>Callable</var> is typically to be used on <var>Functions</var> that perform some action but also return a status or informational value. Since the caller might not care about the informational value, the <var>Callable</var> keyword allows the caller to invoke the <var>Function</var> without a target for the return value.
    class cat
<p>
      public
Without the <var>Callable</var> keyword, an attempt to invoke a <var>Function</var> without a target for the result (the target of an assignment or input to another method or subroutine) causes a compilation error. Not having the <var>Callable</var> option makes sense for a <var>Function</var> whose chief purpose is to return a value, since invoking it without a return value defeats the purpose. For example, if you had: </p>
          function weight is float
<p class="code">class cat
          ...
  public
      end public
      function weight is float
    end class
      ...
  end public
end class
</p>
It probably wouldn't make sense to do:
It probably wouldn't make sense to do:
    %misha  is object cat
<p class="code">%misha  is object cat
    ...
...
    %misha:weight
%misha:weight
In this case, it is probably best not to put the Callable keyword on the function declaration, and to catch likely typos or misunderstandings at compile-time.
</p>
*Properties behave very much like variables, with the exception that they have code that is run when their value is set or retrieved. This behavioral similarity between properties and variables can be formally described as an ''isomorphism'' between properties and variables. Practically, this means that a variable can be changed to a property, and vice versa, without worrying about how the member is used outside the class.<br/><br/>The keywords ''ReadWrite'', ''ReadOnly'', and ''WriteOnly'' indicate the type of access allowed for the property. The default, ''ReadWrite'', indicates that the property can be both set and retrieved. ReadOnly properties can only be retrieved, and WriteOnly properties can only be set.<br/><br/>ReadOnly properties are functionally equivalent to non-Callable Functions: one can call a ReadOnly Property a non-Callable Function, and vice versa, with no change to how the method can be used in applications. This makes the naming of such a method a ReadOnly Property or non-Callable Function primarily a hint to the users or maintainers of a class &mdash; a ReadOnly Property, as the name suggests, is a static attribute of an object, while a non-Callable Function is thought of as performing some dynamic action. Any method that is to be Callable must be a Function rather than a Property.
In this case, it is probably best not to put the <var>Callable</var> keyword on the <var>Function</var> declaration, and to catch likely typos or misunderstandings at compile-time.</td></tr>
*The ''''Public'''', ''''Private'''', and ''''Shared'''' keywords are optional, but, if present, must match the declaration block type. That is, in a Private block, ''Private'' must be indicated; in a Public Shared block, ''Public Shared'' must be indicated; and so on.
 
*Constructors behave like subroutines but cannot be called directly. Instead, they are called by the Janus SOAP User Language Interface when an object is first created.
<tr><th><var>Public</var>, <var>Private</var>, and <var>Shared</var></th>
<td>The <var>Public</var>, <var>Private</var>, and <var>Shared</var> keywords are optional, but, if present, must match the declaration block type. That is, in a <var>Private</var> block, <code>Private</code> must be indicated; in a <var>Public Shared</var> block, <code>Public Shared</code> must be indicated; and so on.</td></tr>
 
<tr><th nowrap><div id="readWrite"></div><var>ReadWrite</var>, <var>ReadOnly</var>, and <var>WriteOnly</var></th>
<td>Properties behave very much like variables, with the exception that they have code that is run when their value is set or retrieved. This behavioral similarity between properties and variables can be formally described as an ''isomorphism'' between properties and variables. Practically, this means that a variable can be changed to a property, and vice versa, without worrying about how the member is used outside the class.
<p>
The keywords <var>ReadWrite</var>, <var>ReadOnly</var>, and <var>WriteOnly</var> indicate the type of access allowed for the property. The default, <var>ReadWrite</var>, indicates that the property can be both set and retrieved. <var>ReadOnly</var> properties can only be retrieved, and <var>WriteOnly</var> properties can only be set. </p>
<p>
<var>ReadOnly</var> properties are functionally equivalent to non-<var>Callable</var> Functions: one can think of a <var>ReadOnly</var> <var>Property</var> as a non-<var>Callable</var> <var>Function</var>, and vice versa, with no change to how the method can be used in applications. This makes the naming of such a method a <var>ReadOnly</var> <var>Property</var> or non-<var>Callable</var> <var>Function</var> primarily a hint to the users or maintainers of a class &mdash; a <var>ReadOnly</var> <var>Property</var>, as the name suggests, is a static attribute of an object, while a non-<var>Callable</var> Function is thought of as performing some dynamic operation. Any method that is to be <var>Callable</var> must be a <var>Function</var> rather than a <var>Property</var>.</p></td></tr>
 
<tr><th><var>Constructor</var></th>
<td><var>Constructors</var> behave like subroutines but cannot be called directly. Instead, they are called by <var class="product">SOUL</var> when an object is first created.</td></tr>
</table>
 
===Declaration block example===
===Declaration block example===
In the following example, a class block consists of one public block which
In the following example, a class block consists of one public block which
contains one variable declaration, one method (subroutine) declaration,
contains one variable declaration, one method (subroutine) declaration,
and a disallow clause:
and a disallow clause:
    class stooge
<p class="code">class stooge
      public
  public
          variable funniness is float
      variable funniness is float
          subroutine slap(%howHard is float)
      subroutine slap(%howHard is float)
          disallow new
      disallow new
      end public
  end public
    end class
end class
</p>
 
==The Auto keyword on object declarations==
==The Auto keyword on object declarations==
While it is not a daunting task to create an instance of an object,
While it is not a daunting task to create an instance of an object,
Line 377: Line 561:
This chore can be simplified by placing the object instantiation
This chore can be simplified by placing the object instantiation
statement immediately after the declaration:
statement immediately after the declaration:
    %stringArray    is object stringList
<p class="code">%stringArray    is object stringList
    %stringArray = new
%stringArray = new
</p>
But this mixes code with variable declarations,
But this mixes code with variable declarations,
which might be deemed ugly, at best, and doesn't work, anyway,
which might be deemed ugly, at best, and doesn't work, anyway,
Line 385: Line 570:
Shared block.
Shared block.
   
   
To get around these issues, the ''''Auto'''' keyword was introduced
To get around these issues, you can use the <var>Auto</var> keyword
for object variable declarations in Sirius Mods version 6.6.
for object variable declarations.
For example:
For example:
    %stringArray  is object stringList  auto new
<p class="code">%stringArray  is object stringList  auto new </p>
   
   
An Auto keyword may be followed by either:
An Auto keyword may be followed by either:
*An enumeration value, for enumeration variables.
*An enumeration value, for enumeration variables.
*An instantiation method,
*An instantiation method, which currently can only be <var>New</var> (with no parameters), for object
which currently can only be ''''New'''' (with no parameters), for object
and collection variables (for classes for which New is valid).
and collection variables (for classes for which New is valid).
Any instantiation that requires
Any instantiation that requires
parameters must be done programmatically rather than on the
parameters must be done programmatically rather than on the
variable declaration (for more information, see :hdref refid=newcons.).
variable declaration (for more information, see [[Object variables#Using New or other Constructors|"Using New or other Constructors"]].).
   
   
The automatic instantiation of an object for a variable whose declaration
The automatic instantiation of an object for a variable whose declaration
includes Auto New does ''not'' use any constructor that may be defined for
includes <var>Auto New</var> does ''not'' use any constructor that may be defined for
the object class, even a constructor named ''''New''''.
the object class, even a constructor named <code>New</code>.
The instantiation is generic, so to speak.
The instantiation is generic, so to speak.
   
   
''Auto New'' is valid for user-defined class object variables
<var>Auto New</var> is valid for user-defined class object variables
only if the class definition
only if the class definition
includes ''Allow Auto''.
includes <var>Allow Auto</var>.


The Auto keyword can be very useful for array-like
The <var>Auto</var> keyword can be very useful for array-like
objects, especially [[Collections|collections]].
objects, especially [[Collections|collections]].
For example, it might seem natural to be able to start adding immediately
For example, it might seem natural to be able to start adding immediately
to a stringlist without actually instantiating it:
to a stringlist without actually instantiating it:
    %colors  is object stringlist  auto new
<p class="code">%colors  is object stringlist  auto new
   
   
    %colors:add('Turquoise')
%colors:add('Turquoise')
    %colors:add('Amber')
%colors:add('Amber')
    ...
... </p>
   
   
Collections seem particularly suited for being immediately added to:
Collections seem particularly suited for being immediately added to:
    %itemPrice  is collection namedArraylist of float auto new
<p class="code">%itemPrice  is collection namedArraylist of float auto new
   
   
    %itemPrice('Widget')  =  22.34
%itemPrice('Widget')  =  22.34
    %itemPrice('Thingy')  =    9.84
%itemPrice('Thingy')  =    9.84
    %itemPrice('Whatsit') =  64.18
%itemPrice('Whatsit') =  64.18
      ...
  ... </p>
   
   
The Auto keyword can also be quite useful for [[Enumerations|enumerations]].
The <var>Auto</var> keyword can also be quite useful for [[Enumerations|enumerations]].
An Auto keyword on an enumeration variable acts very much like
An <var>Auto</var> keyword on an enumeration variable acts very much like
an Initial clause on a String, Float, or Fixed variable:
an <var>Initial</var> clause on a <var>String</var>, <var>Float</var>, or <var>Fixed</var> variable:
    %haveWarned    is enumeration boolean auto false
<p class="code">%haveWarned    is enumeration boolean auto false </p>
   
   
While it might be tempting to think about the Auto clause as
While it might be tempting to think about the <var>Auto</var> clause as
doing the instantiation or assignment &mdash; as if an assignment immediately
doing the instantiation or assignment &mdash; as if an assignment immediately
followed the declaration &mdash. this is not quite the way Auto works.
followed the declaration &mdash; this is not quite the way <var>Auto</var> works.
Auto waits until the first reference to the variable, as
<var>Auto</var> waits until the first reference to the variable, as
long as it is not an assignment to the variable, before assigning to it.
long as it is not an assignment to the variable, before assigning to it.
That is, the following declarations do not cause three Stringlist
That is, the following declarations do not cause three <var>Stringlist</var>
instances to be created:
instances to be created:
    %red      is object stringList
<p class="code">%red      is object stringList
    %white    is object stringList
%white    is object stringList
    %blue    is object stringList
%blue    is object stringList </p>
   
   
But if somewhat further down, the following statement is executed,
But if somewhat further down, the following statement is executed,
a Stringlist instance is automatically created before the Add
a <var>Stringlist</var> instance is automatically created before the <var>Add</var>
method is invoked:
method is invoked:
    %blue:add('Azure')
<p class="code">%blue:add('Azure') </p>
If the first reference to an <var>Auto</var> object is an assignment:
<p class="code">%red = %blue </p>
the <var>Auto</var> attribute will never be exercised, even if the assignment
source is <var>Null</var>, and even if the object is subsequently discarded.
 
<p class="note"><b>Note:</b> The effect of <var>Auto</var> is restricted to the first reference to a variable in a request. </p>
 
==Member reference==
Defining a class entails defining its members, that is,
variables and methods.  In order to access one of those variables or invoke one of those methods, an
expression must identify the member, and:
<ul>
<li>An object instance, for a non-shared, non-constructor member
<li>The member's class, for a constructor or shared member
(as described below, in some circumstances the class is implicit in the expression)
</ul>
Here are three typical examples of member reference:
<ul>
<li>an object instance, for a non-shared, non-constructor member
<li>the member's class, for a constructor or shared member
(as described below, in some circumstances the class is implicit in the expression)
</ul>
Here are three typical examples of member reference:
<ul>
<li><p class="code" style="display:inline;">print %doc:value</p>
<p style="margin-top:.75em">The <var>Value</var> function is invoked using the object <code>%doc</code>.</p></li>
 
<li><p class="code" style="display:inline;">%slis = New</p>
<p style="margin-top:.75em">A new instance object of the <var>Stringlist</var> class is assigned to the object variable <code>%slis</code>.  Note that here the class identification is implicit.</p></li>
 
<li><p class="code" style="display:inline;">%num = %(daemon):parentNumber</p>
<p style="margin-top:.75em">The shared <var>ParentNumber</var> property is invoked to return the user number of the parent of the current thread.
<code>%(daemon)</code> identifies the class of the shared property. </p></li>
</ul>
 
All of the above cases use the following syntax:
<p class="syntax"><span class="squareb">[</span><var class="term">identifyingPhrase</var><span class="literal">:</span><span class="squareb">]</span><var class="term">member</var>
</p>
The colon (<b>:</b>) preceding the <var class="term">member</var> may optionally be preceded or followed by any
number of blanks; here are some examples:
<p class="code">%str = %doc:Value('/order/cust/@name')
%name = %custInfo : name
%addr = %custInfo: -
  address
%phone = %custInfo -
  :phone
</p>
The '<var class="term">identifyingPhrase</var><span class="literal">:</span>' phrase is optional when invoking
a <var>Constructor</var> or virtual constructor, in some circumstances, as described [[#Constructors or shared members|below]].
<ul>
<li>If '<var class="term">identifyingPhrase</var><span class="literal">:</span>' is present, the
<var class="term">member</var> phrase may either be a member name or a [[Method variables|method variable]].
<li>If '<var class="term">identifyingPhrase</var><span class="literal">:</span>' is not present, the
<var class="term">member</var> phrase must be a member name.
</ul>
===Non-shared, non-constructor members===
For class members that are neither shared nor constructors, the member name (or method variable)
must be preceded by an expression that
denotes an object of the class, and a colon.
The immediately preceeding examples are all of non-shared/non-constructor method invocation (<code>...:Value...</code>) or
class variable access (<code>%custInfo:...</code>).
For method invocation, the object instance (above, <code>%doc</code>) is [[Object oriented programming in SOUL#methobj|method object]]
input to the method.
For variable access, the class variable (<code>name</code>, <code>address</code>, <code>phone</code>) for the object instance (<code>%custInfo</code>) is accessed.
===Constructors or shared members===
Since neither a constructor nor a shared member operates on an object instance, referencing a
constructor or a shared member
requires identifying only the class to which the member belongs.
For constructors and shared members, either of the following two syntax forms can always be used:
<ul>
<li>Explicitly citing the class name:
<p class="syntax"><span class="term">%(className)</span><span class="literal">:</span><span class="term">member</span>
</p>
<p style="margin-top:.75em">For example:</p>
<p class="code">%tok = %(system):[[Arguments (System function)|arguments]]:stringTokenizer
</p></li>
<li>Implicitly identifying the class name:
<p class="syntax"><span class="term">object</span><span class="literal">:</span><span class="term">member</span>
</p>
<p>For example:</p>
<p class="code">%daem object daemon
if %daem:[[AmDaemon (Daemon property)|amDaemon]] then ...
</p>
<p>
In this form of identifying the class, note that since no specific object instance is operated upon, the value of the method object (<code>%daem</code>) may be <var>Null</var>.</p></li>
</ul>
 
In addition to the above two syntax forms for identifying the class, in some circumstances
the class can be implicitly identified for a <var>Constructor</var> or virtual constructor
(a shared <var>Function</var>, <var>Property</var>, or <var>Variable</var> whose type
is an object in the class):
   
   
If the first reference to an Auto object is an assignment:
<ul>
    %red = %blue
<li>The first, most common, set
of circumstances is when the constructor is on the right hand side of an assignment statement:
<p class="code"><span class="term">%var</span> <span class="literal">=</span> <span class="term">constructor</span>
</p>
Note that <code>%var</code> must be an object variable, and <code>constructor</code>       
must be a <var>Constructor</var> or virtual constructor in the class of <code>%var</code>. 
   
   
the Auto attribute will never be exercised, even if the assignment
For example, using the <var>New</var> <var>Constructor</var>:
source is Null, and even if the object is subsequently discarded.
<p class="code">%strLis object stringlist
%strLis = new
</p>
A shared <var>Function</var> example is:
<p class="code">%doc object xmlDoc
...
for each record ...
  %doc = newFromRecord
</p>
<li>The class identification may also be omitted before a constructor if
the constuctor's result is an argument (but not the method object) of another method.
For example:
<p class="code">%origLis:[[CopyItems (Stringlist function)|copyItems]]([[List (Stringlist function)|list]]('a', 'b', 'c'))
</p>
In this example, the result of the virtual constructor
<var>List</var> is the <var>Stringlist</var> argument of the
<var>CopyItems</var> method.
<li>A final, albeit rare,
set of circumstances is when the result of the constructor is the right-hand comparand, for example  in an <var>If</var>
statement:
<p class="code"><span class="literal">If</span> <span class="term">object</span> <span class="term">eqOrNe</span> <span class="term">constructor</span> <span class="literal">Then</span>
</p>
'''Note:'''
<ul>
<li><code>object</code> must be an object
expression, and <code>constructor</code> must be a <var>Constructor</var> or virtual constructor in the class of <code>object</code>.
<li>The comparison operator (<code>eqOrNe</code>) must be one of <var>Eq</var>, <var>=</var>, or <var>Ne</var>.
<li><code>object</code> may not itself be a constructor without a preceding
<var class="term">identifyingPhrase</var> and colon.
<li>Only the second operand in a comparison may use implicit class specification, that is, you '''''may not''''' do:
<p class="code"><span class="literal">If</span> <span class="term">constructor</span> <span class="term">eqOrNe</span> <span class="term">object</span> <span class="literal">Then</span>
</p>
</ul>
</ul>
 
===Base class members===
If an object expression is for an [[Inheritance and polymorphism|extension]] class, and you want to refer to
a non-inherited member of a base class, you must specify the parenthesized base class name before the
member name (or method variable).  For example:
<p class="code">class ropeList extends stringList
...
end class
%rope object ropeList
%rope = new
%rope:(stringlist)add('sisal')
</p>
===Intrinsic methods===
As with non-intrinsic methods, you use the following syntax to invoke an [[List of Intrinsic methods|intrinsic method]]:
<p class="syntax"><span class="term">identifyingPhrase</span><span class="literal">:</span><span class="term">method</span>
</p>
For intrinsic methods, however, the <var class="term">identifyingPhrase</var> and colon (<tt>:</tt>) are always
required; there is no circumstance that allows implicit class identification.
An example of invoking an intrinsic method is:
<p class="code">%sqrt = 169:SquareRoot
</p>
In addition, there are certain unusual syntax rules for invoking intrinsic methods, as described in
[[Intrinsic classes#Intrinsic method syntax: special cases|"Intrinsic method syntax: special cases"]].
 
==See also==
<ul>
<li>[[Object variables]]
<li>[[Copying objects]]
<li>[[Global and session objects]]
<li>[[Managing server space for objects]]
<li>[[Methods]]
<li>[[Object oriented programming in SOUL]]
<li>[[Getting started with OOP for User Language programmers]]
</ul>


Note: the effect of Auto is restricted to the first reference to a variable
[[Category:Overviews]]
in a request.
[[Category:SOUL object-oriented programming topics]]

Latest revision as of 04:50, 18 June 2019

Classes

A class is a collection of attributes that describe an object and of operations that may be performed on an object. The attributes of an object are formally declared as class variables, while the operations are declared as methods. In SOUL, there are four types of methods: subroutines, functions, properties, and constructors. While a class describes general attributes and methods, a specific instance of the class is called an object, and variables that refer to those instances are called object variables.

The description of a class, all variables, and all methods of the class must be contained inside one or more Class blocks:

begin ... class car ... end class ... class car ... end class car ... end

This is slightly different from most other object-oriented languages, which require an entire class definition to be within a single class block (though sometimes a keyword other than Class is used to mark the start of the class definition). The main reason for allowing multiple class blocks is that they let you selectively include methods in a class as they are needed in a request, rather than having to compile all the methods in a class if any one is needed.

For example, Subroutine Paint for Class Car could be kept in a procedure as follows:

PROCEDURE CLASS.CAR.PAINT !dupexit class car subroutine paint(%color is string len 32) ... end subroutine end class END PROCEDURE

!dupexit causes the procedure to be exited on all but the first Include, which ensures that the subroutine is only defined once. Attempting to define it more than once would result in compilation errors, of course.

Given the above procedure, code that needs to use Subroutine Paint in class Car could simply Include procedure CLASS.CAR.PAINT:

include class.car.paint ... %chevy is object car ... %chevy:paint('Teal')

Subroutine Paint can only be defined after it has been declared inside a declaration block for class Car. If a class is broken up in such a way that some methods are in their own procedures, it is likely that the class's declaration blocks would all be contained in another procedure, called something like CLASS.CAR in this example. It would be necessary to Include that procedure before the procedure that defines the Paint method:

include class.car include class.car.paint ... %chevy is object car ... %chevy:paint('Teal')

Class block syntax

The full syntax of the Class block is:

[Local] Class className [Abstract] - [Alias=aliasList] - [Exception] - [Extends=extendList] - [Private] - [RequiredSuffix=suffix] [declaration blocks] [method definitions] End Class [className]

Syntax terms

Local Indicates that the class can only be referenced inside the containing scope. A class declared inside a method or complex subroutine must be Local. A class in the outermost (Begin/End) scope can be either local, in which case it cannot be referenced in other scopes such as methods or complex subroutines, or not local, in which case it can be referenced anywhere in the request.
className Consists of any valid alphanumeric and other non-separator characters.
Abstract Indicates that the class is abstract, that is, no instance of the class can be created unless it is an instance of a non-abstract extension class.
Alias=aliasList A list of aliases for the class, with each alias separated by the word And. See Aliases for class names.
Exception Indicates that the class is an exception class, that is an instance it can be thrown.
Extends=extendList A list of classes that this class extends.
Private Indicates that the class is a private class, meaning that only the class itself and friends can reference class members. Private is only available in Model 204 7.6 and later. See Friend declarations.
RequiredSuffix=suffix A non-quoted string of characters that must end the name of any object variable being declared for the class; provides a way of enforcing naming standards for class variable names.
declaration blocks Declarations for all variables and methods in a class. See Declaration blocks.
method definitions The definitions of the methods in the class. See Method definition syntax.

The (one or more) class blocks for a class contain the declaration blocks and method definitions for the class, which together comprise the class definition. The class definition consists of zero or more declaration blocks and zero or more method definitions. It is valid to define a class with no declarations and no methods, though such a class is not going to be particularly useful.

Multiple class blocks for a class are, in effect, combined to form one class definition. Class blocks after the first may exactly repeat any of the Alias, Extends, or RequiredSuffix clauses from the first block for a class, but they may not change these clauses.

Classes must be declared outside of any other block structures such as For loops, If blocks, or other Class blocks.

Once a class is declared, you use the Object keyword on variable declarations to declare objects as instances of a class:

%malibu is object car %outback is object car

Colons in class names

The colon character (:) is allowed in SOUL class names. The following, for example, is a valid class declaration:

class customer:order

No colon-demarcated part of a class name can be the word System. For example, System, System:foo, Foo:system, and Foo:system:bar are all invalid class names.

While there is no special meaning to the colon-demarcated parts of a class name, the intent is that the colons be used to group classes, and that higher-level qualifiers come before lower-level ones. For example, in class name MiddleEarth:rohan:rider, a Rider class falls under a grouping of classes qualified by Rohan. This grouping, itself, falls under the larger grouping of MiddleEarth. Using the colon in this way is likely to make it easier to take advantage of future class management features that depend on a hierarchical, colon-based, class naming scheme.

Aliases for class names

As of Sirius Mods Version 7.8, you can define one or more alias names for an existing user class. You do so by specifying an Alias list in the class declaration. For example, you are changing a naming convention for some code, and you want to refer to user-defined class Blue by another name, say Indigo. Your class declaration would be:

class blue alias indigo

You can then use either the primary class name or the alias when declaring objects of the class:

%foo is object blue %bar is object indigo

An object of the alias class is compiled as an object of the primary class, and the runtime class of an object variable that was defined using an alias is the primary class. Consequently, all system messages you receive will specify the primary class.

For example, given the declarations above, if you call method TouchUp for an object declared for the Indigo class:

%bar:touchUp

And method TouchUp does not exist in the class, the error message you receive is:

MSIR.0733: Member TOUCHUP not found in class Blue

While this might seem potentially confusing, aliases are intended primarily for migrating class names, so any confusion will be limited to this migration period. In addition, only the owner of a class can declare an alias, so aliases are not likely to proliferate in your site's code.

Multiple aliases in the alias list are separated by the keyword And.

Declaration blocks

Declaration blocks are another feature of SOUL that is unusual among object-oriented languages. Declaration blocks describe all the variables and methods that make up a class, but they contain no code.

Most object-oriented languages allow variable declarations of all types to be mixed arbitrarily with method definitions of all types. Though this appears to be very convenient, it can make it difficult for users or maintainers of a class to determine the general framework of the class. SOUL requires all variables and methods of a class to be declared in a few blocks, and in those blocks, to be organized in ways that are significant to their behavior.

SOUL allows no more than one of each of four declaration blocks:

  • Public
  • Private
  • Public Shared
  • Private Shared

The Public block contains the declaration of all class members (variables that are associated with an instance of the class, and methods that operate on an instance of the class) that are "public", that is available to users of the class. The Private block contains the declaration of all members that are "private", that is available only to methods inside a class definition.

The Shared blocks contain members that are shared among all instances of the class and so are able to be referenced independent of any instance of the class. Public Shared members are available to all users of the class, and Private Shared members are available only within the class definition. Shared variables and methods are discussed in more detail in Shared class members.

The declaration blocks contain declarations for all variables and methods in a class, and they must appear before any method definitions.

Declaration block syntax

The format of the declarations blocks is:

{Public | Private} [Shared]
   [Allow clauses]
   [Disallow clauses]
   [Friend declarations]
   [member declarations]
End {Public | Private} [Shared] [className]

Syntax terms

Public | PrivateIndicates whether the block describes members that can be referenced outside the class (Public) or members that can only be referenced inside the class (Private) or by friend classes.
Allow clausesIndicate special operations allowed by the class. They can only be specified in a Public (non-shared) block, and consist of the Allow keyword followed by Auto, Copy, DeepCopy, or Narrow. See Allow clause, below.

There can be multiple Allow clauses in a declaration block.

Allow clauses are optional and can be interspersed with Disallow clauses, Friend declarations, and member declarations.
Disallow clausesIndicate operations not allowed by the class. They can only be specified in a Public (non-shared) block, and consist of the Disallow keyword followed by Discard, or New. See Disallow clause, below.

There can be multiple Disallow clauses in a declaration block.

Disallow clauses are optional and can be interspersed with Allow clauses, Friend declarations, and member declarations.
Friend declarationsIndicate friends of a class. Friends of a class are other classes that can access private members of the class or, in the case of a public friend, access public members of a private class. They can only be specified in a Private Shared block or the Public Shared block of a private class. In the latter case, a friend declared in a Public Shared block is called a public friend.

Friend declarations consist of the Friend keyword followed by the name of another class. There can be multiple Friend declarations in a declaration block. Friend clauses are optional and can be interspersed with Allow clauses, Disallow clauses, and member declarations.

Note: Friend classes do not have to have been declared, nor do they ever have to be declared inside a request.

Friend declarations are available in Model 204 7.6 and later.

member declarationsDeclare variables or methods in the class. These are described in Variable declarations and Method declarations and method types, below.

While member declarations are optional, they tend to appear in almost all declarations blocks; and a class without member declarations in some declaration blocks would be pretty much useless.

Member declaration clauses can be interspersed with Allow clauses, Disallow clauses, and Friend declarations.
EndMarks the end of the declaration block. It must be followed immediately by Public or Private which is then possibly followed by Shared. The keywords following End must exactly match the block type: for a Public block, End Public is required; for a Private Shared block, End Private Shared is required. The class name can, optionally, be indicated after the block type on the End clause.

Allow clause

As the name suggests, the Allow clause indicates things one is allowed to do with the object instances of the class that one ordinarily would be allowed to do:

Allow Auto Indicates that an Auto New clause may be used on the declaration of variables of the type of the class being defined. If the class is an extension class, Allow Auto must also be specified on the class's base class(es).
Allow Copy or Allow DeepCopy Makes available the system Copy or DeepCopy method, respectively, for objects in the class.
Allow Narrow Permits narrowing assignments for objects in the class.

Allow Auto and constructors

Prior to version 7.6 of the Sirius Mods, a class defined with Allow Auto is not allowed to contain a constructor. As of version 7.6, Allow Auto is allowed in a class that has a constructor. However, for an object variable declared with Auto New in such a class, an automatic instantiation of the object does not call any of the constructors in the class, even if the class has defined an explicit constructor with the name New.

For example, the following request prints %y:b = 0 — the class's New constructor does not get called by %y:b:

b class y public allow auto variable b is float constructor new end public   constructor new %this:b = 1234 end constructor   end class   %y is object y auto new printText {~} = {%y:b} end

Disallow clause

As the name suggests, the Disallow clause indicates things that one is not allowed to do with the object instances of the class that ordinarily would be allowed:

Disallow DiscardIndicates that objects of the class cannot be explicitly discarded outside the class. Objects of the class can still be explicitly Discarded inside the class. For more about discarding objects, see "Discarding objects"
Disallow NewIndicates that explicit construction of instances of the class using the New constructor is not allowed outside the class. Objects of the class can still be explicitly created with New inside the class, and other public constructors for the class can be invoked outside the class. For more about object constructors, see "Creating object instances"

Friend declarations

The Friend clause declares another class that can access private members in the class being declared or, if inside a Public Shared block (Private class only), another class that can access public members in the class being declared. The class declared as a friend need not have been declared, nor does it ever need to be declared inside a request. A class can only ever have friends in the same scope, so a Friend statement inside a Local Class declaration only applies to classes in the scope that contains the local class.

In the following example, class Moe is a friend of class Curly so can access private variables in class Curly:

class curly public variable weight is float variable nyucks is float variable lastName is string len 32 end public private variable painlevel is float end private private shared friend moe end private shared end class class moe public variable salary is float variable oughtas is float variable lastName is string len 32 variable partner is object curly subroutine smackPartner end public subroutine smackPartner %this:partner:painLevel = @ + 5 end subroutine end class

If Moe had not been declared as a friend of Curly then subroutine smackPartner in class Moe would not have been able to reference the PainLevel member of class Curly.

Note that if we change the declaration of Curly to be Private, Moe could continue to access both public and private members of Curly since it is declared inside the private Shared block. However, in that case, a class could be declared as a friend inside a Public Shared block, creating a public friend that can only access public members of Curly. Public friends are only allowed for Private classes. In the following, Larry is a public friend of Curly so can reference public members:

class curly private public variable weight is float variable nyucks is float variable lastName is string len 32 end public private variable painlevel is float end private private shared friend moe end private shared public shared friend larry end public shared end class class larry public variable hairLength is float variable partner is object curly subroutine amusePartner end public subroutine amusePartner %this:partner:nyucks = @ + 2 end subroutine end class

Note that non-friends can always reference instances of private classes, they simply cannot reference class members. So the following is allowed:

class curly private public variable weight is float variable nyucks is float variable lastName is string len 32 end public private variable painlevel is float end private private shared friend moe end private shared public shared friend larry end public shared end class %curly is object curly %moe is object moe ... %curly = %moe:partner

Variable declarations

Variable declarations in a class declaration block must begin with Variable followed by the variable name (without a percent sign). This is followed by any standard SOUL variable declaration, with the exception that:

  • Static is not valid in a non-Shared block.
  • Global is not valid in a non-Shared block and requires the global name in a Shared block.
  • Common is not allowed.
  • The keywords to indicate "publicity" (Private, Private Shared, Public, and Public Shared) are allowed. If specified, they must match the publicity of the enclosing declaration block.
  • Auto followed by New or an enumeration value is allowed.
  • For ReadOnly and Protected variable declarations, like standard Model 204 variable declarations, the keyword Is immediately after the variable name on variable declarations is optional.

Variable declaration examples

An example follows of a class block with some variable declarations:

class car public variable price is fixed dp 2 variable make is string len 32 variable description is longstring end public end class

Variable declaration references

Class variables can be referenced with an instance of that class, that is, with an object variable, like %gremlin below:

%gremlin is object car ... printText The price is {%gremlin:price}

The colon (:) that separates the object variable %gremlin and the class member variable price, above, may alternatively be specified with a one or more blanks before and/or after it.

ReadOnly and Protected variables

It is possible to to declare a variable as ReadOnly or Protected. A ReadOnly variable can be examined by code outside the class, but it can only be updated inside the class. A Protected variable can be examined by code outside the class, but it can only be updated inside the class or by code inside an extension class. Since a ReadOnly or Protected variable must be accessible outside the class, it would make no sense for such a variable to be in a Private section, so this is not allowed.

ReadOnly and Protected variables are useful for providing an efficient means of accessing relatively static information about objects in a class. Unlike a property, retrieving ReadOnly or Protected variables requires no code to be run in the class.

The term Protected is something of a misnomer. In a very real sense, Protected variables aren't protected, at all. After all, they can be updated by extension classes. Their real purpose is to act as a sort of overridable variable, that is, a value that can be overridden by an extension class.

For example, suppose a class has a ReadOnly variable called PartNumber. Perhaps PartNumber is set by the class's constructor, and then it is never set again (for a particular object instance). Now, suppose this class wants to allow extension classes to set a different PartNumber, probably in their constructors. One approach would be to make PartNumber an Overridable ReadOnly property. But this is a somewhat heavyweight approach, as it requires a bit of code (the property Get method) for each extension class, and this code has to be run every time PartNumber is retrieved. Instead, PartNumber could be made a Protected variable, and an extension class's constructor could simply set it after calling the base class constructor. This allows the extension classes to override the base class's PartNumber using little extra code and using a very efficient access path for the value.

Protected variables differ from Overridable ReadOnly properties, however, in that an Overridable ReadOnly property always guarantees that the extension class's code is run, so it overrides the base class's properties. With a Protected variable, it would be possible for an extension class to set it, and for the base class to then set it to something else, undoing the extension class action.

Because they can be updated by both base and extension classes, Protected variables are probably most useful for very static values, like values that are only set by the constructors. Use of Protected variables that can be set throughout the life of objects of a class is likely to be error-prone, as it requires careful coordination of updates between the base and extension classes.

Method declarations and method types

In addition to variable declarations, a declaration block can also contain method declarations. There are four basic types of methods:

FunctionA method that produces an output value.
SubroutineA method that produces no output value.
PropertyA method that sometimes produces an output value and that can sometimes be set to a value, that is, be on the left side of an assignment statement.
ConstructorA method that processes objects immediately after the objects are instantiated. A Constructor produces no output value.

Method declaration syntax

The declarations for the four types of methods are very similar:

Function name - [(parameters)] [Is] datatype - [AllowNullObject] [CurrentRecord context] [Callable] - [Public | Private [Shared] ]   Subroutine name - [(parameters)] - [AllowNullObject] [CurrentRecord context] - [Public | Private [Shared] ]   Property name - [(parameters)] [Is] datatype - [ReadWrite | ReadOnly | WriteOnly] - [AllowNullObject] [CurrentRecord context] - [Public | Private [Shared] ]   Constructor name - [(parameters)] - [Public | Private]

Syntax terms

name The name for a Function, Subroutine, Property, or Constructor must follow the same rules as variable names and cannot be the same as another member of the class, whether a variable or method, public or private, shared or non-shared.
parameters Method parameters have the same format as the parameter declarations of complex subroutines. For example:

class car public ... subroutine wash(%detergent is string len 16, - %howLong is float, - %howClean is fixed output) ... end public end class

datatype
Functions and properties require a datatype, that is, an Is clause (though the keyword Is is optional).
  • For functions, the datatype is the datatype returned by the function.
  • For properties, the datatype is both the datatype returned by the property (when the property is used as an input) and the datatype to which the property is set. For example, suppose class Car had a mileage property:

    class car public ... property mileage is float ... end public end class

    You could do:

    %civic is object car ... %civic:mileage = 32.6 ... %hybridMileage = %civic:mileage + 19

AllowNullObject The AllowNullObject keyword on a method declaration indicates that, even though the method operates on an instance of the class (an object), the method may be invoked with a null object variable. Ordinarily, a non-shared method referenced by a null object variable causes a (null-object reference) request cancellation error. Invoking a method even when the object variable is null may be useful in diagnostic code that displays object status or cleanup code.

Since a Constructor will always receive the just-created object as the object variable, AllowNullObject is not allowed on Constructor declarations.

CurrentRecord The CurrentRecord clause (CurrentRecord In File <name>, or CurrentRecord In Group <name>) on a method declaration indicates that the method may only be invoked in a record context (established by a For Each Record loop, for example) for the declared file or group. Furthermore, CurrentRecord establishes a record context within the method, so method statements may reference a record field without having to be wrapped inside a record For loop (such as a For Record CurrentRecord loop).
Callable
The Callable keyword, available only on Function declarations, indicates that the function can be Called, that is, invoked without a target for the result. Such a function may be Called even though it returns a value, and the Call may be explicit or implicit.

class car public ... function wash is float callable ... end public end class ... %beetle is object car ... %beetle:wash

The last line can also be written:

call %beetle:wash

Callable is typically to be used on Functions that perform some action but also return a status or informational value. Since the caller might not care about the informational value, the Callable keyword allows the caller to invoke the Function without a target for the return value.

Without the Callable keyword, an attempt to invoke a Function without a target for the result (the target of an assignment or input to another method or subroutine) causes a compilation error. Not having the Callable option makes sense for a Function whose chief purpose is to return a value, since invoking it without a return value defeats the purpose. For example, if you had:

class cat public function weight is float ... end public end class

It probably wouldn't make sense to do:

%misha is object cat ... %misha:weight

In this case, it is probably best not to put the Callable keyword on the Function declaration, and to catch likely typos or misunderstandings at compile-time.
Public, Private, and Shared The Public, Private, and Shared keywords are optional, but, if present, must match the declaration block type. That is, in a Private block, Private must be indicated; in a Public Shared block, Public Shared must be indicated; and so on.
ReadWrite, ReadOnly, and WriteOnly
Properties behave very much like variables, with the exception that they have code that is run when their value is set or retrieved. This behavioral similarity between properties and variables can be formally described as an isomorphism between properties and variables. Practically, this means that a variable can be changed to a property, and vice versa, without worrying about how the member is used outside the class.

The keywords ReadWrite, ReadOnly, and WriteOnly indicate the type of access allowed for the property. The default, ReadWrite, indicates that the property can be both set and retrieved. ReadOnly properties can only be retrieved, and WriteOnly properties can only be set.

ReadOnly properties are functionally equivalent to non-Callable Functions: one can think of a ReadOnly Property as a non-Callable Function, and vice versa, with no change to how the method can be used in applications. This makes the naming of such a method a ReadOnly Property or non-Callable Function primarily a hint to the users or maintainers of a class — a ReadOnly Property, as the name suggests, is a static attribute of an object, while a non-Callable Function is thought of as performing some dynamic operation. Any method that is to be Callable must be a Function rather than a Property.

Constructor Constructors behave like subroutines but cannot be called directly. Instead, they are called by SOUL when an object is first created.

Declaration block example

In the following example, a class block consists of one public block which contains one variable declaration, one method (subroutine) declaration, and a disallow clause:

class stooge public variable funniness is float subroutine slap(%howHard is float) disallow new end public end class

The Auto keyword on object declarations

While it is not a daunting task to create an instance of an object, sometimes it can be unclear exactly where to instantiate an object. Furthermore, certain objects just do not make sense as nulls, so instantiating them is simply a chore. This chore can be simplified by placing the object instantiation statement immediately after the declaration:

%stringArray is object stringList %stringArray = new

But this mixes code with variable declarations, which might be deemed ugly, at best, and doesn't work, anyway, if the object is actually inside a class — code can't be placed in side a Public, Private, Public Shared, or Private Shared block.

To get around these issues, you can use the Auto keyword for object variable declarations. For example:

%stringArray is object stringList auto new

An Auto keyword may be followed by either:

  • An enumeration value, for enumeration variables.
  • An instantiation method, which currently can only be New (with no parameters), for object

and collection variables (for classes for which New is valid). Any instantiation that requires parameters must be done programmatically rather than on the variable declaration (for more information, see "Using New or other Constructors".).

The automatic instantiation of an object for a variable whose declaration includes Auto New does not use any constructor that may be defined for the object class, even a constructor named New. The instantiation is generic, so to speak.

Auto New is valid for user-defined class object variables only if the class definition includes Allow Auto.

The Auto keyword can be very useful for array-like objects, especially collections. For example, it might seem natural to be able to start adding immediately to a stringlist without actually instantiating it:

%colors is object stringlist auto new %colors:add('Turquoise') %colors:add('Amber') ...

Collections seem particularly suited for being immediately added to:

%itemPrice is collection namedArraylist of float auto new %itemPrice('Widget') = 22.34 %itemPrice('Thingy') = 9.84 %itemPrice('Whatsit') = 64.18 ...

The Auto keyword can also be quite useful for enumerations. An Auto keyword on an enumeration variable acts very much like an Initial clause on a String, Float, or Fixed variable:

%haveWarned is enumeration boolean auto false

While it might be tempting to think about the Auto clause as doing the instantiation or assignment — as if an assignment immediately followed the declaration — this is not quite the way Auto works. Auto waits until the first reference to the variable, as long as it is not an assignment to the variable, before assigning to it. That is, the following declarations do not cause three Stringlist instances to be created:

%red is object stringList %white is object stringList %blue is object stringList

But if somewhat further down, the following statement is executed, a Stringlist instance is automatically created before the Add method is invoked:

%blue:add('Azure')

If the first reference to an Auto object is an assignment:

%red = %blue

the Auto attribute will never be exercised, even if the assignment source is Null, and even if the object is subsequently discarded.

Note: The effect of Auto is restricted to the first reference to a variable in a request.

Member reference

Defining a class entails defining its members, that is, variables and methods. In order to access one of those variables or invoke one of those methods, an expression must identify the member, and:

  • An object instance, for a non-shared, non-constructor member
  • The member's class, for a constructor or shared member (as described below, in some circumstances the class is implicit in the expression)

Here are three typical examples of member reference:

  • an object instance, for a non-shared, non-constructor member
  • the member's class, for a constructor or shared member (as described below, in some circumstances the class is implicit in the expression)

Here are three typical examples of member reference:

  • print %doc:value

    The Value function is invoked using the object %doc.

  • %slis = New

    A new instance object of the Stringlist class is assigned to the object variable %slis. Note that here the class identification is implicit.

  • %num = %(daemon):parentNumber

    The shared ParentNumber property is invoked to return the user number of the parent of the current thread. %(daemon) identifies the class of the shared property.

All of the above cases use the following syntax:

[identifyingPhrase:]member

The colon (:) preceding the member may optionally be preceded or followed by any number of blanks; here are some examples:

%str = %doc:Value('/order/cust/@name') %name = %custInfo : name %addr = %custInfo: - address %phone = %custInfo - :phone

The 'identifyingPhrase:' phrase is optional when invoking a Constructor or virtual constructor, in some circumstances, as described below.

  • If 'identifyingPhrase:' is present, the member phrase may either be a member name or a method variable.
  • If 'identifyingPhrase:' is not present, the member phrase must be a member name.

Non-shared, non-constructor members

For class members that are neither shared nor constructors, the member name (or method variable) must be preceded by an expression that denotes an object of the class, and a colon. The immediately preceeding examples are all of non-shared/non-constructor method invocation (...:Value...) or class variable access (%custInfo:...). For method invocation, the object instance (above, %doc) is method object input to the method. For variable access, the class variable (name, address, phone) for the object instance (%custInfo) is accessed.

Constructors or shared members

Since neither a constructor nor a shared member operates on an object instance, referencing a constructor or a shared member requires identifying only the class to which the member belongs. For constructors and shared members, either of the following two syntax forms can always be used:

  • Explicitly citing the class name:

    %(className):member

    For example:

    %tok = %(system):arguments:stringTokenizer

  • Implicitly identifying the class name:

    object:member

    For example:

    %daem object daemon if %daem:amDaemon then ...

    In this form of identifying the class, note that since no specific object instance is operated upon, the value of the method object (%daem) may be Null.

In addition to the above two syntax forms for identifying the class, in some circumstances the class can be implicitly identified for a Constructor or virtual constructor (a shared Function, Property, or Variable whose type is an object in the class):

  • The first, most common, set of circumstances is when the constructor is on the right hand side of an assignment statement:

    %var = constructor

    Note that %var must be an object variable, and constructor must be a Constructor or virtual constructor in the class of %var.

    For example, using the New Constructor:

    %strLis object stringlist %strLis = new

    A shared Function example is:

    %doc object xmlDoc ... for each record ... %doc = newFromRecord

  • The class identification may also be omitted before a constructor if the constuctor's result is an argument (but not the method object) of another method. For example:

    %origLis:copyItems(list('a', 'b', 'c'))

    In this example, the result of the virtual constructor List is the Stringlist argument of the CopyItems method.

  • A final, albeit rare, set of circumstances is when the result of the constructor is the right-hand comparand, for example in an If statement:

    If object eqOrNe constructor Then

    Note:

    • object must be an object expression, and constructor must be a Constructor or virtual constructor in the class of object.
    • The comparison operator (eqOrNe) must be one of Eq, =, or Ne.
    • object may not itself be a constructor without a preceding identifyingPhrase and colon.
    • Only the second operand in a comparison may use implicit class specification, that is, you may not do:

      If constructor eqOrNe object Then

Base class members

If an object expression is for an extension class, and you want to refer to a non-inherited member of a base class, you must specify the parenthesized base class name before the member name (or method variable). For example:

class ropeList extends stringList ... end class %rope object ropeList %rope = new %rope:(stringlist)add('sisal')

Intrinsic methods

As with non-intrinsic methods, you use the following syntax to invoke an intrinsic method:

identifyingPhrase:method

For intrinsic methods, however, the identifyingPhrase and colon (:) are always required; there is no circumstance that allows implicit class identification.

An example of invoking an intrinsic method is:

%sqrt = 169:SquareRoot

In addition, there are certain unusual syntax rules for invoking intrinsic methods, as described in "Intrinsic method syntax: special cases".

See also