Classes and Objects: Difference between revisions
No edit summary |
|||
(91 intermediate revisions by 7 users not shown) | |||
Line 2: | Line 2: | ||
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 | ||
operations 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 operations are declared as methods. | In <var class="product">[[SOUL]]</var>, there are four types of methods: subroutines, | ||
In | |||
functions, properties, and constructors. | functions, properties, and constructors. | ||
While a class | 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]]. | |||
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 | must be contained inside one or more <var>Class</var> blocks: | ||
<p class="code">begin | |||
... | |||
class car | |||
... | |||
end class | |||
... | |||
class car | |||
... | |||
end class car | |||
... | |||
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 | 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: | ||
<p class="code">PROCEDURE CLASS.CAR.PAINT | |||
!dupexit | |||
class car | |||
subroutine paint(%color is string len 32) | |||
... | |||
end subroutine | |||
end class | |||
END PROCEDURE | |||
</p> | |||
exited on all but the first | <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>: | ||
<p class="code">include class.car.paint | |||
... | |||
%chevy is object car | |||
... | |||
%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 | 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: | ||
<p class="code">include class.car | |||
include class.car.paint | |||
... | |||
%chevy is object car | |||
... | |||
%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: | ||
<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> - | |||
<span class="squareb">[</span>Alias=<span class="term">aliasList</span><span class="squareb">]</span> - | |||
<span class="squareb">[</span>Exception<span class="squareb">]</span> - | |||
<span class="squareb">[</span>Extends=<span class="term">extendList</span><span class="squareb">]</span> - | |||
<span class="squareb">[</span>Private<span class="squareb">]</span> - | |||
<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> | |||
===Syntax terms=== | |||
<table class="syntaxTable"> | |||
<tr><th><var>Local</var></th> | |||
<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> | |||
<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 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> | |||
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''. | <td>Declarations for all variables and methods in a class. See [[#Declaration blocks|Declaration blocks]]. </td></tr> | ||
The class definition consists of zero or more ''declaration blocks'' and | |||
zero or more ''method definitions''. | <tr><th>method definitions</th> | ||
It is valid to define a class with no declarations and no | <td>The definitions of the methods in the class. See [[Methods#Method definition syntax|Method definition syntax]]. </td></tr> | ||
methods, though such a class is not going to be particularly useful. | </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. | 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. | ||
Classes must be declared outside of any other block structures such as For loops, If blocks, or other Class blocks. | 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. | ||
Once a class is declared, | Once a class is declared, you use the <var>Object</var> keyword on variable declarations to declare objects as instances of a class: | ||
you use the Object keyword on variable declarations to | <p class="code">%malibu is object car | ||
declare objects as instances of a class: | %outback is object car | ||
</p> | |||
===Colons in class names=== | ===Colons in class names=== | ||
The colon character (:) is allowed in | 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: | ||
<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. | |||
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, | 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 | 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. | |||
For example, given the declarations above, if you call method <code>TouchUp</code> for an object declared for the Indigo class: | |||
<p class="code">%bar:touchUp | |||
</p> | |||
And method <code>TouchUp</code> does not exist in the class, the error message you receive is: | |||
<p class="code">MSIR.0733: Member TOUCHUP not found in class Blue | |||
</p> | |||
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 <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 | '''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. | |||
in a few blocks, and in those blocks, to be organized in ways that are | |||
significant to their behavior. | |||
<var class="product">SOUL</var> allows no more than one of each of four declaration blocks: | |||
<ul> | |||
<li><var>Public</var> | |||
<li><var>Private</var> | |||
<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 | 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. | ||
<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. | |||
<var>Shared</var> 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 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: | ||
{Public | Private} [Shared] | |||
[Allow clauses] | |||
[Disallow clauses] | |||
[Friend declarations] | |||
[member declarations] | |||
End {Public | Private} [Shared] [className] | |||
Syntax | |||
===Syntax terms=== | |||
<table class="syntaxTable"> | |||
<tr><th><var>Public | 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> | |||
<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. | |||
<p> | |||
There can be multiple <var>Allow</var> clauses in a declaration block. </p> | |||
<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 | 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: | ||
<table class="syntaxTable"> | |||
<tr><th><var>Allow Auto</var></th> | |||
<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> | |||
<tr><th><var>Allow Copy</var> or | |||
<var>Allow DeepCopy</var></th> | |||
<td>Makes available the system <var>Copy</var> or <var>DeepCopy</var> method, respectively, for objects in the class.</td></tr> | |||
<tr><th><var>Allow Narrow</var></th> | |||
<td>Permits narrowing assignments for objects in the class.</td></tr> | |||
</table> | |||
====Allow Auto and constructors==== | |||
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>. | |||
For example, the following request prints <code>%y:b = 0</code> — the class's <code>New</code> constructor does '''not''' get called by <code>%y:b</code>: | |||
<p class="code">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 | |||
[[Targeted Text statements|printText]] {~} = {%y:b} | |||
end | |||
</p> | |||
===Disallow clause=== | ===Disallow clause=== | ||
As the name suggests, the | 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: | ||
; | <table class="syntaxTable"> | ||
; | <tr><th><var>Disallow 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 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 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 | |||
<li><var>Global</var> is not valid in a non-Shared block and requires the global name in a Shared block. </li> | |||
<li><var>Common</var> is not allowed. </li> | |||
<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> | |||
<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 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: | ||
</p> | |||
<p class="code">class car | |||
public | |||
variable price is fixed dp 2 | |||
variable make is string len 32 | |||
variable description is longstring | |||
end public | |||
Class variables can be referenced | end class | ||
</p> | |||
====Variable declaration references==== | |||
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. | |||
====ReadOnly and Protected variables==== | |||
and | It is possible to to declare a variable as <var>ReadOnly</var> or <var>Protected</var>. | ||
A <var>ReadOnly</var> variable can be examined by code outside the class, but it can only | |||
as | |||
A | |||
be updated inside the class. | be updated inside the class. | ||
A | 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 | 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=== | ||
Line 277: | Line 416: | ||
contain ''method declarations''. | contain ''method declarations''. | ||
There are four basic types of methods: | There are four basic types of methods: | ||
<table class="syntaxTable"> | |||
<tr><th><var>[[Methods#Function|Function]]</var></th><td>A method that produces an output value.</td></tr> | |||
<tr><th><var>[[Methods#Subroutine|Subroutine]]</var></th><td>A method that produces no output value.</td></tr> | |||
<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==== | ||
The declarations for the four types of methods are very similar: | |||
<p class="syntax">Function <span class="term">name</span> - | |||
[(<span class="term">parameters</span>)] [Is] <span class="term">datatype</span> - | |||
[AllowNullObject] [CurrentRecord <span class="term">context</span>] [Callable] - | |||
[Public | Private [Shared] ] | |||
| |||
Subroutine <span class="term">name</span> - | |||
[(<span class="term">parameters</span>)] - | |||
[AllowNullObject] [CurrentRecord <span class="term">context</span>] - | |||
[Public | Private [Shared] ] | |||
| |||
Property <span class="term">name</span> - | |||
[(<span class="term">parameters</span>)] [Is] <span class="term">datatype</span> - | |||
[ReadWrite | ReadOnly | WriteOnly] - | |||
[AllowNullObject] [CurrentRecord <span class="term">context</span>] - | |||
[Public | Private [Shared] ] | |||
| |||
Constructor <span class="term">name</span> - | |||
[(<span class="term">parameters</span>)] - | |||
Syntax | [Public | Private] | ||
</p> | |||
====Syntax terms==== | |||
<table class="syntaxTable"> | |||
<tr><th>name</th> | |||
<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. | |||
</td></tr> | |||
<tr><th>parameters</th> | |||
<td>Method parameters have the same format as the parameter declarations of complex subroutines. For example: | |||
<p class="code">class car | |||
public | |||
... | |||
subroutine wash(%detergent is string len 16, - | |||
%howLong is float, - | |||
%howClean is fixed output) | |||
... | |||
end public | |||
end class | |||
</p></td></tr> | |||
<tr><th><div id="datatype"></div>datatype</th> | |||
<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: | ||
<p class="code">%civic is object car | |||
... | ... | ||
%civic:mileage = 32.6 | |||
... | ... | ||
%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: | ||
<p class="code">call %beetle:wash</p> | |||
<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. | |||
<p> | |||
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> | |||
<p class="code">class cat | |||
public | |||
function weight is float | |||
... | |||
end public | |||
end class | |||
</p> | |||
It probably wouldn't make sense to do: | It probably wouldn't make sense to do: | ||
<p class="code">%misha is object cat | |||
... | |||
%misha:weight | |||
In this case, it is probably best not to put the Callable keyword on the | </p> | ||
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> | |||
<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 — 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=== | ||
Line 368: | Line 545: | ||
contains one variable declaration, one method (subroutine) declaration, | contains one variable declaration, one method (subroutine) declaration, | ||
and a disallow clause: | and a disallow clause: | ||
<p class="code">class stooge | |||
public | |||
variable funniness is float | |||
subroutine slap(%howHard is float) | |||
disallow new | |||
end public | |||
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 382: | 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: | ||
<p class="code">%stringArray is object stringList | |||
%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 390: | Line 570: | ||
Shared block. | Shared block. | ||
To get around these issues, the | To get around these issues, you can use the <var>Auto</var> keyword | ||
for object variable declarations | for object variable declarations. | ||
For example: | For example: | ||
<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, which currently can only be | *An instantiation method, which currently can only be <var>New</var> (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 | 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 | the object class, even a constructor named <code>New</code>. | ||
The instantiation is generic, so to speak. | The instantiation is generic, so to speak. | ||
<var>Auto New</var> is valid for user-defined class object variables | |||
only if the class definition | only if the class definition | ||
includes | 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: | ||
<p class="code">%colors is object stringlist auto new | |||
%colors:add('Turquoise') | |||
%colors:add('Amber') | |||
... </p> | |||
Collections seem particularly suited for being immediately added to: | Collections seem particularly suited for being immediately added to: | ||
<p class="code">%itemPrice is collection namedArraylist of float auto new | |||
%itemPrice('Widget') = 22.34 | |||
%itemPrice('Thingy') = 9.84 | |||
%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: | ||
<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 — as if an assignment immediately | doing the instantiation or assignment — as if an assignment immediately | ||
followed the declaration — this is not quite the way Auto works. | followed the declaration — 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: | ||
<p class="code">%red is object stringList | |||
%white 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: | ||
<p class="code">%blue:add('Azure') </p> | |||
If the first reference to an Auto object is an assignment: | If the first reference to an <var>Auto</var> object is an assignment: | ||
<p class="code">%red = %blue </p> | |||
the Auto attribute will never be exercised, even if the assignment | the <var>Auto</var> attribute will never be exercised, even if the assignment | ||
source is Null, and even if the object is subsequently discarded. | 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): | |||
<ul> | |||
<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>. | |||
For example, using the <var>New</var> <var>Constructor</var>: | |||
<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> | |||
[[Category:Overviews]] | |||
[[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 | Private | Indicates 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 clauses | Indicate 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 clauses | Indicate 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 declarations | 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 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 declarations | Declare 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. |
End | Marks 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 Discard | Indicates 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 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. 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:
Function | A method that produces an output value. |
---|---|
Subroutine | A method that produces no output value. |
Property | 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. |
Constructor | A 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).
|
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.
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.
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, andconstructor
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, andconstructor
must be a Constructor or virtual constructor in the class ofobject
.- 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".