Method variables: Difference between revisions
No edit summary |
|||
(16 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
==Summary== | ==Summary== | ||
<var class="product">SOUL</var> supports '''method variables''', variables that reference methods. | |||
They can be used to invoke varying methods with a particular, unchanging invocation. | They can be used to invoke varying methods with a particular, unchanging invocation. | ||
For example, to conditionally invoke the system intrinsic [[Right (String function)|Right]], [[Left (String function)|Left]], or [[Center (String function)|Center]] method, you can do something like the following: | For example, to conditionally invoke the system intrinsic <var>[[Right (String function)|Right]]</var>, <var>[[Left (String function)|Left]]</var>, or <var>[[Center (String function)|Center]]</var> method, you can do something like the following: | ||
<p class="code">%justify is function (string):justify(%amount is float) is longstring | |||
... | ... | ||
if %x then | |||
%justify = right | |||
elseif %y then | |||
%justify = centre | |||
else | |||
%justify = left | |||
end if | |||
... | ... | ||
[[Targeted Text statements|printtext]] {'Whatever':%justify(10)} | |||
printtext {%something:%justify(10)} </p> | |||
As shown in the preceding example, to use a method variable you first declare it, then assign a method to it, then invoke it. | As shown in the preceding example, to use a method variable you first declare it, then assign a method to it, then invoke it. | ||
The declaration specifies the type of method to which the method variable may refer. | The declaration specifies the type of method to which the method variable may refer. | ||
<var>Function</var> and <var>Subroutine</var> are the two type options. | |||
A Function variable is required if the method (or class variable) you want to assign to the variable returns a value (this is always true for class variables). | A <var>Function</var> variable is required if the method (or class variable) you want to assign to the variable returns a value (this is always true for class variables). | ||
A Subroutine variable is used if the method to be assigned does not return a value or is Callable. | A <var>Subroutine</var> variable is used if the method to be assigned does not return a value or is <var>[[Notation conventions for methods#Calling functions|Callable]]</var>. | ||
The method you assign to a method variable can be a system method, a | The method you assign to a method variable can be a system method, a user-defined method, (instance method, enhancement method, shared method, or local method), or a class variable. | ||
Invoking the method using the method variable employs the same syntax | Invoking the method using the method variable employs the same syntax | ||
as invoking the method directly, except that the method variable is used. | as invoking the method directly, except that the method variable is used. | ||
Line 31: | Line 29: | ||
The following example shows assignment from a user class method, class variables, | The following example shows assignment from a user class method, class variables, | ||
and a local method: | and a local method: | ||
< | <p class="code">b | ||
b | |||
class junk | class junk | ||
Line 66: | Line 63: | ||
end | end | ||
</ | </p> | ||
The result is: | The result is: | ||
< | <p class="code">%rubbish:%rfunc:right(20, pad='*') = *******************3 | ||
%rubbish:%rfunc:right(20, pad='*') = *******************3 | |||
%rubbish:%rfunc = Holy cow! | %rubbish:%rfunc = Holy cow! | ||
%rubbish:%rfunc = 27 | %rubbish:%rfunc = 27 | ||
%rubbish:%rfunc = 81 | %rubbish:%rfunc = 81 | ||
</ | </p> | ||
'''Note:''' | |||
Because of loose datatyping for intrinsics, a method variable declared as Float can return a String (or | <blockquote class="note"> | ||
Unicode) value, if the method assigned to it returns such a value. | '''Note:''' The second line of the result contains string output. | ||
Because of loose datatyping for intrinsics, a method variable declared as <var>Float</var> can return a <var>String</var> (or | |||
<var>Unicode</var>) value, if the method assigned to it returns such a value. | |||
<p> | |||
Notice also that class <var>Variables</var> are used exactly like the <var>Functions</var> in the examples above. </p> | |||
<var>Function</var> or <var>Subroutine</var> variables may not be passed between requests; they are always nullified between requests. | |||
<var>Function</var> or <var>Subroutine</var> variables may not appear inside of global objects. | |||
Function or Subroutine variables may not be passed between requests; they are always nullified between requests. | </blockquote> | ||
Function or Subroutine variables may not appear inside of global objects. | |||
==Declaring a method variable== | ==Declaring a method variable== | ||
The syntax for the declaration of a method variable is shown below: | The syntax for the declaration of a method variable is shown below: | ||
< | <p class="syntax"><span class="term">%var</span> [Is] <span class="term">methodTemplate</span> | (<span class="term">methodTemplate</span>) [<span class="term">methvarQualifiers</span>] | ||
%var [Is] (methodTemplate) | </p> | ||
</ | |||
Where: | Where: | ||
< | <table> | ||
< | <tr><th>%var</th> | ||
< | <td>The name of the method variable, which can be any name that follows the rules for <var class="product">SOUL</var> %variables. </td></tr> | ||
< | <tr><th>methodTemplate</th> | ||
< | <td>The [[#Method template in method variable declaration|function or subroutine declaration template]]. </td></tr> | ||
< | <tr><th>methvarQualifiers</th> | ||
< | <td>Qualifiers for the variable being declared, such as <var>Common</var> or <var>Shared</var>. </td></tr> | ||
If there are any method variable qualifiers, | <p> | ||
For example if a method variable is Shared, that qualifier must be specified as in: | If there are any method variable qualifiers, <var class="term">methodTemplate</var> '''must''' be enclosed in parentheses. | ||
< | For example if a method variable is <var>Shared</var>, that qualifier must be specified as in: </p> | ||
%x is (function (stringlist):stupid is float) shared | <p class="code">%x is (function (stringlist):stupid is float) shared | ||
</ | </p> | ||
</ | </table> | ||
===Method template in method variable declaration=== | ===Method template in method variable declaration=== | ||
A method declaration template is used to declare the type of a method variable and is essentially the same as a normal method declaration, consisting of the method name followed by the method description. | A method declaration template is used to declare the type of a method variable and is essentially the same as a normal method declaration, consisting of the method name followed by the method description. | ||
< | <p class="syntax"><span class="term">methodType</span> <span class="squareb">[</span>(<span class="term">class</span>):<span class="squareb">]</span><span class="term">methodName</span><span class="squareb">[</span>(<span class="term">parms</span>)<span class="squareb">]</span> - | ||
methodType [(class):]methodName[(parms)] - | <span class="squareb">[</span>Is <span class="term">returnType</span><span class="squareb">]</span> <span class="squareb">[</span><span class="term">methodQualifiers</span><span class="squareb">]</span> | ||
[Is returnType] [<methodQualifiers>] | </p> | ||
</ | <table> | ||
< | <tr><th>methodType</th> | ||
< | <td>The type of method that is to be represented by the method variable. | ||
< | The options are <var>Function</var> and <var>Subroutine</var>. | ||
The options are | <var>Function</var> is required if the method (or class variable) returns a value. | ||
Function is required if the method (or class variable) returns a value. | <var>Subroutine</var> is used if the method does not return a value or is <var>[[Notation conventions for methods#Calling functions|Callable]]</var>. | ||
Subroutine is used if the method does not return a value or is Callable. | <p> | ||
Strictly speaking, <var>Function</var> and <var>Subroutine</var> are the classes of the method object that is is referenced by the method variable. | |||
Strictly speaking, | There are currently no methods in the <var>Function</var> or <var>Subroutine</var> class. </p></td></tr> | ||
There are currently no methods in the Function or Subroutine class. | |||
< | <tr><th>class</th> | ||
< | <td>Identifies the [[#Method class in method variable method template|class of objects to which the method applies]]. </td></tr> | ||
< | |||
< | <tr><th>methodName</th> | ||
< | <td>The method name, which can be any name that follows the rules for <var class="product">SOUL</var> %variables, is preceded by its class as necessary, and is followed by its parameters, if any. </td></tr> | ||
< | |||
< | <tr><th>parms</th> | ||
< | <td>The parameters that are used in invoking the method variable. </td></tr> | ||
This is only allowed for | |||
< | <tr><th>returnType</th> | ||
< | <td>The datatype of values returned by the methods assigned to the method variable. | ||
Since the method operation details tend to come from the method to which the variable is set, | This is only allowed for <var>Function</var> class method variables. </td></tr> | ||
</ | |||
<tr><th>methodQualifiers</th> | |||
<td>Details of method operation. | |||
Since the method operation details tend to come from the method to which the variable is set, <var class="term">methodQualifiers</var> is rarely used. </td></tr> | |||
</table> | |||
===Method class in method variable method template=== | ===Method class in method variable method template=== | ||
Two method variables that are identically declared except for their class specifications are '''not''' equivalent. | Two method variables that are identically declared except for their class specifications are '''not''' equivalent. | ||
That is, given these declarations: | That is, given these declarations: | ||
< | <p class="code">%foo is function (stringlist):hash is float | ||
%foo is function (stringlist):hash is float | |||
%bar is function (stringlist):hash is float | %bar is function (stringlist):hash is float | ||
%junk is function (xmlDoc):hash is float | %junk is function (xmlDoc):hash is float | ||
</ | </p> | ||
The following is valid: | The following is valid: | ||
< | <p class="code">%foo = %bar | ||
%foo = %bar | </p> | ||
</ | |||
But the following is '''not valid''' | But the following is '''''not valid''''': | ||
< | <p class="code">%foo = %junk | ||
%foo = %junk | </p> | ||
</ | |||
The class designation supplies context for any method assigned to the method variable. | The class designation supplies context for any method assigned to the method variable. | ||
Given the declarations in the previous item, the following assignment implies the Count method in the Stringlist class: | Given the declarations in the previous item, the following assignment implies the <var>Count</var> method in the <var>Stringlist</var> class: | ||
< | <p class="code">%foo = count | ||
%foo = count | </p> | ||
</ | |||
Count above references the Stringlist Count method unless you had created a local Stringlist enhancement method called Count, in which case method %foo references the local Count method. | <code>Count</code> above references the <var>Stringlist</var> class <var>Count</var> method unless you had created a local <var>Stringlist</var> enhancement method called "Count," in which case method <code>%foo</code> references the local <code>Count</code> method. | ||
Omitting a class specification implies that the method referred to by the method variable does not operate on an instance of an object variable, that is, it is a shared method. | Omitting a class specification implies that the method referred to by the method variable does not operate on an instance of an object variable, that is, it is a shared method. | ||
Such a method variable may be assigned to a conforming ''shared'' method in any class. | Such a method variable may be assigned to a conforming '''shared''' method in any class. | ||
For example, the following declaration specifies a method variable | For example, the following declaration specifies a method variable | ||
that does not apply to an object instance: | that does not apply to an object instance: | ||
< | <p class="code">%debug is subroutine debug | ||
%debug is subroutine debug | </p> | ||
</ | |||
Consequently, valid assignments could take any of the following forms: | Consequently, valid assignments could take any of the following forms: | ||
< | <p class="code">%debug = (myclass):sharedSubroutine | ||
%debug = (myclass):sharedSubroutine | |||
%debug = (yourclass):sharedSubroutine | %debug = (yourclass):sharedSubroutine | ||
%debug = localMethod | %debug = localMethod | ||
%debug = (object):garbageCollect | %debug = (object):garbageCollect | ||
</ | </p> | ||
===Method parameter list and other qualifiers in method variable method template=== | ===Method parameter list and other qualifiers in method variable method template=== | ||
The method parameter list can contain required and optional, named and unnamed parameters, and their types. | The method parameter list can contain required and optional, named and unnamed parameters, and their types. | ||
The parameter list is followed by a method result datatype (for functions) and then possibly by further qualifiers. | The parameter list is followed by a method result datatype (for functions) and then possibly by further qualifiers. | ||
For example, a Calculate method in Myclass that takes a numeric value | For example, a <code>Calculate</code> method in <code>Myclass</code> that takes a numeric value | ||
and returns a numeric value is declared as: | and returns a numeric value is declared as: | ||
< | <p class="code">function (myclass):calculate(%x is float) is float | ||
function (myclass):calculate(%x is float) is float | </p> | ||
</ | |||
A method variable declared with a return type that is a | A method variable declared with a return type that is a <var class="product">SOUL</var> [[Intrinsic classes|intrinsic datatype]] might return another intrinsic type when invoked if you assign a method to it that returns the other type. | ||
This is because | This is because <var class="product">SOUL</var> allows the assignment from one intrinsic type to another. | ||
Most method qualifiers such as AllowNullObject or Implements are derived from the method assigned to a method variable and make no sense on the method template in a method variable declaration. | Most method qualifiers such as <var>AllowNullObject</var> or <var>Implements</var> are derived from the method assigned to a method variable and make no sense on the method template in a method variable declaration. | ||
In fact, there are currently no method qualifiers other than the return datatype that are allowed in the method template in a method variable declaration. | In fact, there are currently no method qualifiers other than the return datatype that are allowed in the method template in a method variable declaration. | ||
This includes | This includes <var>Throws</var> clauses, though you ''may'' assign a method that throws exceptions to a method variable and catch exceptions thrown by that method. | ||
===Method variable declaration example=== | ===Method variable declaration example=== | ||
In the following example, the | In the following example, the <code>%stooge</code> <var>Function</var> variable is declared for the <code>Mayhem</code> class: | ||
< | <p class="code">class mayhem | ||
class mayhem | |||
public | public | ||
variable moe is float | variable moe is float | ||
Line 192: | Line 197: | ||
function curly is float | function curly is float | ||
end public | end public | ||
end class | end class | ||
%stooge is function (mayhem):Number is float | %stooge is function (mayhem):Number is float | ||
</ | </p> | ||
In this declaration | |||
< | In this declaration, <code>function</code> is used for the object declaration, indicating that the method returns a value. The name <code>Number</code> is actually nothing but a placeholder, and the above declaration is considered identical to: | ||
%stooge is function (mayhem):Placehold is float | <p class="code">%stooge is function (mayhem):Placehold is float | ||
</ | </p> | ||
Some placeholder is needed in the Function name position to make the declaration read properly, and you may want to put a meaningful name there to suggest the intent of the method. | |||
But whether the name is specified as | Some placeholder is needed in the <var>Function</var> name position to make the declaration read properly, and you may want to put a meaningful name there to suggest the intent of the method. | ||
< | But whether the name is specified as <code>Number</code> or as <code>Placehold</code>, the following assignments are all valid: | ||
%stooge = moe | <p class="code">%stooge = moe | ||
%stooge = larry | %stooge = larry | ||
%stooge = curly | %stooge = curly | ||
</ | </p> | ||
The method values to which the method variable in this example refers are in fact class Variables, which fit the %stooge declaration: they are function-like, operating on a Mayhem object and returning a Float. | |||
The method values to which the method variable in this example refers are in fact class <var>Variables</var>, which fit the %stooge declaration: they are function-like, operating on a <code>Mayhem</code> object and returning a <var>Float</var>. | |||
If the method in the declaration has parameters, as in: | If the method in the declaration has parameters, as in: | ||
< | <p class="code">%stooge is function (mayhem):meaningless(%x is float) is float | ||
%stooge is function (mayhem):meaningless(%x is float) is float | </p> | ||
</ | |||
The parameter names ''do not'' get used, so the above declaration | The parameter names ''do not'' get used, so the above declaration | ||
is equivalent to this one: | is equivalent to this one: | ||
< | <p class="code">%stooge is function (mayhem):meaningful(%y is float) is float | ||
%stooge is function (mayhem):meaningful(%y is float) is float | </p> | ||
</ | |||
Thus, if two method variables contain references to methods on a Mayhem object that take a float input parameter and return a float, they can be assigned to each other, as in the following: | Thus, if two method variables contain references to methods on a <code>Mayhem</code> object that take a float input parameter and return a float, they can be assigned to each other, as in the following: | ||
< | <p class="code">%stooge is function (mayhem):meaningless(%x is float) is float | ||
%stooge is function (mayhem):meaningless(%x is float) is float | |||
%stud is function (mayhem):meaningful(%y is float)is float | %stud is function (mayhem):meaningful(%y is float)is float | ||
... | ... | ||
%stooge = %stud | %stooge = %stud | ||
</ | </p> | ||
For NameAllowed or NameRequired parameters, the parameter names | |||
For <var>NameAllowed</var> or <var>NameRequired</var> parameters, the parameter names ''are'' meaningful in the method object declarations. | |||
==Assigning to a method variable== | ==Assigning to a method variable== | ||
You assign a ''method value'' to a method variable using the following syntax: | You assign a '''method value''' to a method variable using the following syntax: | ||
< | <p class="syntax"><span class="term">%var</span> = [(+<span class="term">classname</span>)]<span class="term">method</span> | ||
%var = [(+classname)]method | </p> | ||
</ | |||
Where: | Where: | ||
< | <table> | ||
< | <tr><th>%var</th> | ||
< | <td>The name of the method variable.</td></tr> | ||
< | |||
< | <tr><th>classname</th> | ||
<td>The name of the class to which the method variable applies. | |||
This name is necesary if the method is a shared method or an enhancement method; | This name is necesary if the method is a shared method or an enhancement method; | ||
otherwise, the method is assumed to be a member of the class specified in the method variable declaration. | otherwise, the method is assumed to be a member of the class specified in the method variable declaration. | ||
The plus sign is necessary for an enhancement method. | The plus sign is necessary for an enhancement method.</td></tr> | ||
< | |||
< | <tr><th>method</th> | ||
A method name can be either a | <td>The name of the method to which the method variable will refer or an [[#Anonymous functions|anonymous function]]. | ||
</ | A method name can be either a <var class="product">SOUL</var> method (instance method, enhancement method, shared method, or local method), a class member (<var>Variable</var> or read-only <var>Property</var>), or a method variable that fits the [[#method template in method variable declaration]]. </td></tr> | ||
</table> | |||
The source of an assignment to a method variable can even be the invocation of a function that returns a method value that fits the method description. | The source of an assignment to a method variable can even be the invocation of a function that returns a method value that fits the method description. | ||
Of course, it can also be another, compatible method variable. | Of course, it can also be another, compatible method variable. | ||
You may assign a method that takes no parameters to a method variable that is declared as referring to a method with a parameter. | You may assign a method that takes no parameters to a method variable that is declared as referring to a method with a parameter. | ||
For example, if function variable | For example, if function variable <code>%goo</code> is declared as: | ||
< | <p class="code">%whynot is function (silly):whatever(%x is float) is float | ||
%whynot is function (silly):whatever(%x is float) is float | </p> | ||
</ | The following assignment is valid if <code>Z</code> is a variable in class <code>Silly</code>: | ||
The following assignment is valid if Z is a variable in class Silly: | <p class="code">%whynot = Z | ||
< | </p> | ||
%whynot = Z | |||
</ | In such a case, the parameter, <code>%x</code>, in the method variable declaration is ignored when the method variable is invoked. | ||
In such a case, the parameter, %x, in the method variable declaration is ignored when the method variable is invoked. | For example, if <code>%whynot</code> was set to <code>Silly</code> class variable <code>Z</code>, the <code>22</code> in the following invocation of <code>%whynot</code> would be ignored: | ||
For example, if | <p class="code">printText {~} = {%sillyVar:%whynot(22)} | ||
< | </p> | ||
printText {~} = {%sillyVar:%whynot(22)} | |||
</ | |||
This is allowed because methods are allowed to ignore a parameter value. | This is allowed because methods are allowed to ignore a parameter value. | ||
Similarly, say a function in class Silly is defined as: | Similarly, say a function in class <code>Silly</code> is defined as: | ||
< | <p class="code">function stupid(%whatever is float, - | ||
function stupid(%whatever is float, - | |||
%z is longstring optional) is longstring | %z is longstring optional) is longstring | ||
</ | </p> | ||
Then Stupid may also be validly assigned to %whynot: | Then <code>Stupid</code> may also be validly assigned to <code>%whynot</code>: | ||
< | <p class="code">%whynot = stupid | ||
%whynot = stupid | </p> | ||
</ | |||
Method variables follow the standard rules for NameAllowed and NameRequired parameters. | And whenever <code>Stupid</code> is invoked via <code>%whynot</code>, the optional parameter <code>%z</code> will not be present. | ||
Method variables follow the standard rules for <var>NameAllowed</var> and <var>NameRequired</var> parameters. | |||
If a method variable declaration specifies a method with one of these types of parameter, you may not assign to the variable a method that does not have the same parameter name type. | If a method variable declaration specifies a method with one of these types of parameter, you may not assign to the variable a method that does not have the same parameter name type. | ||
A method variable may be assigned to an enhancement method. | A method variable may be assigned to an enhancement method. | ||
The following statement assigns to %whynot the | The following statement assigns to <code>%whynot</code> the <var>Float</var> enhancement method in class <code>Util</code> called <code>BigOnes</code> which returns a <var>Float</var> object: | ||
< | <p class="code">%whynot = (+util)bigOnes | ||
%whynot = (+util)bigOnes | </p> | ||
</ | |||
If a method variable is declared as applying to a base class, you may '''not''' assign an extension class method to it. | If a method variable is declared as applying to a base class, you may '''not''' assign an extension class method to it. | ||
The method variable must be applicable to all objects of the base class, and setting it to a method of a specific extension class would violate this principle. | The method variable must be applicable to all objects of the base class, and setting it to a method of a specific extension class would violate this principle. | ||
You may assign an overridable method to a method variable. | |||
Method variables may be assigned to the special value ''This'', which is an identity method that simply returns its method object. | Method variables may be assigned to the special value '''This''', which is an identity method that simply returns its method object. | ||
For example, given the following code fragment: | For example, given the following code fragment: | ||
< | <p class="code">%justify is function (string):justify(%amount is float) is longstring | ||
%justify is function (string):justify(%amount is float) is longstring | |||
... | ... | ||
if %x then | if %x then | ||
Line 305: | Line 310: | ||
printtext {'Whatever':%justify(10)} | printtext {'Whatever':%justify(10)} | ||
printtext {%something:%justify(10)} | printtext {%something:%justify(10)} | ||
</ | </p> | ||
If the Else logical path is followed, the | |||
If the <code>Else</code> logical path is followed, the <code>printText</code> statement will simply print the string to which it is applied, without justification or padding. | |||
The | The <var>This</var> value is especially useful for sorting and finding maximum and minimum values in collections of intrinsic objects. | ||
<var>This</var> can also be used as an [[#Anonymous functions|anonymous function]] where the anonymous function specifies processing to be applied to the method object. | |||
==Invoking a method variable== | ==Invoking a method variable== | ||
The syntax for invoking a method via a method variable that applies to objects of a class is: | The syntax for invoking a method via a method variable that applies to objects of a class is: | ||
< | <p class="syntax"><span class="term">objectVar</span>:<span class="term">%methodVar</span>[:<span class="term">moreMethods</span>] | ||
objectVar:%methodVar[:moreMethods] | </p> | ||
</ | |||
The syntax for invoking a method via a method variable that doesn't apply to objects of a class is: | The syntax for invoking a method via a method variable that doesn't apply to objects of a class is: | ||
< | <p class="syntax">%(Local):<span class="term">%methodVar</span>[:<span class="term">moreMethods</span>] | ||
%(Local):%methodVar[:moreMethods] | </p> | ||
</ | |||
Where: | Where: | ||
< | <table> | ||
< | <tr><th>objectVar</th> | ||
< | <td>The object variable to which the method variable, <var class="term">%methodVar</var>, is applied. </td></tr> | ||
< | |||
< | <tr><th>%methodVar</th> | ||
< | <td>The name of the method variable. </td></tr> | ||
< | |||
This is no different from stringing a method to the result of a non-variable method. | <tr><th>moreMethods</th> | ||
</ | <td>Additional methods that may be strung together, that is, applied to the result of the <var class="term">%methodVar</var> method. | ||
This is no different from stringing a method to the result of a non-variable method. </td></tr> | |||
</table> | |||
===Method variable invocation examples=== | ===Method variable invocation examples=== | ||
In the following example, method variable | In the following example, method variable <code>%foo</code> has this declaration: | ||
< | <p class="code">%foo is function (stringlist):subset is object stringlist | ||
%foo is function (stringlist):subset is object stringlist | </p> | ||
</ | |||
This declaration indicates that %foo must contain a reference to a method that operates on a Stringlist object and returns a Stringlist object. | This declaration indicates that <code>%foo</code> must contain a reference to a method that operates on a <var>Stringlist</var> object and returns a <var>Stringlist</var> object. | ||
The following local function is defined in the same scope as %foo, after which the function is assigned to %foo: | The following local function is defined in the same scope as <code>%foo</code>, after which the function is assigned to <code>%foo</code>: | ||
< | <p class="code">local function (stringlist):everyOther is object stringlist | ||
local function (stringlist):everyOther is object stringlist | |||
%outlist is object stringlist | %outlist is object stringlist | ||
%i is float | %i is float | ||
Line 348: | Line 355: | ||
... | ... | ||
%foo = everyOther | %foo = everyOther | ||
</ | </p> | ||
To invoke the EveryOther method against Stringlist variable | |||
< | To invoke the <code>EveryOther</code> method against <var>Stringlist</var> variable <code>%list</code>, you apply method variable <code>%foo</code>: | ||
%list = %list:%foo | <p class="code">%list = %list:%foo | ||
</ | </p> | ||
To invoke the EveryOther method against %list and then print the result, you use: | |||
< | To invoke the <var>EveryOther</var> method against <code>%list</code> and then print the result, you use: | ||
%list:%foo:print | <p class="code">%list:%foo:print | ||
</ | </p> | ||
Stringing methods together can be taken further. | Stringing methods together can be taken further. | ||
To invoke EveryOther on %list, then apply EveryOther to the result of | To invoke <code>EveryOther</code> on <code>%list</code>, then apply <code>EveryOther</code> to the result of | ||
that, and finally print the output of those operations: | that, and finally print the output of those operations: | ||
< | <p class="code">%list:%foo:%foo:print | ||
%list:%foo:%foo:print | </p> | ||
</ | |||
In the following example, a method variable that is set to a | In the following example, a method variable that is set to a <var>Shared</var> class variable is invoked using <code>%(local)</code>, the syntax for invoking a local shared method. | ||
The method variable is not applied to a variable because the method, itself, doesn't apply to objects of any class. | The method variable is not applied to a variable because the method, itself, doesn't apply to objects of any class. | ||
< | |||
begin | <p class="code">begin | ||
class Reply | class Reply | ||
Line 389: | Line 397: | ||
end | end | ||
</ | </p> | ||
This request prints | |||
This request prints: <code>Error</code>. | |||
==Anonymous functions== | ==Anonymous functions== | ||
An [http://en.wikipedia.org/wiki/Anonymous_function anonymous function], as the name suggests, is a method that has no name. | An [http://en.wikipedia.org/wiki/Anonymous_function anonymous function], as the name suggests, is a method that has no name. | ||
Because these functions have no name, they must typically be defined in the context in which they are actually used. | Because these functions have no name, they must typically be defined in the context in which they are actually used. | ||
Hopefully, the meaning of this statement is made clear below. | Hopefully, the meaning of this statement is made clear below. | ||
<!-- Support for anonymous functions was added in Sirius Mods version 7.8. --> | |||
Consider the case where a method variable is defined as: | |||
<p class="code">%transform is function (string):transform is longstring | |||
</p> | |||
So, <code>%transform</code> can refer to a method that takes a <var>String</var> input and produces a <var>String</var> output. | |||
So, one can set <code>%transform</code> to the system <var>[[Reverse (String function)|Reverse function]]</var>: | |||
<p class="code">%transform = reverse | |||
</p> | |||
And then invoke the <var>Reverse</var> function via the method variable: | |||
< | <p class="code">printText {~} = {'We are the worms':%transform} | ||
</p> | |||
which produces: | |||
<p class="code">'We are the worms':%transform = smrow eht era eW | |||
</p> | |||
</ | But, suppose you wanted <code>%transform</code> to be set to a method that returns the rightmost three characters of the method object string. | ||
< | |||
printText {~} = {'We are the worms':%transform} | |||
</ | |||
which produces | |||
< | |||
'We are the worms':%transform = smrow eht era eW | |||
</ | |||
But, suppose you wanted %transform to be set to a method that returns the rightmost three characters of the method object string. | |||
Intuitively, it would seem that you should be able to do: | Intuitively, it would seem that you should be able to do: | ||
< | <p class="code">%transform = right(3) | ||
%transform = right(3) | </p> | ||
</ | |||
But, before Sirius Mods 7.8, this was not allowed | But, <!-- before Sirius Mods 7.8-->formerly, this was not allowed because there is no method called <code>Right(3)</code>, only <code>Right</code>. And, one couldn't do: | ||
<p class="code">%transform = right | |||
And, one couldn't do: | </p> | ||
< | |||
%transform = right | because <code>right</code> requires a parameter, but the method variable template for <code>%transform</code> has no parameters. | ||
</ | And, even if the above were allowed, it is still missing the important bit of information about how many of the rightmost bytes <code>%transform</code> should return. | ||
because | |||
And, even if the above were allowed, it | |||
Now, however, this '''is''' allowed: | |||
< | <p class="code">%transform = right(3) | ||
%transform = right(3) | </p> | ||
</ | |||
It is worth understanding exactly what this means. | |||
The compiler sees | The compiler sees <code>right(3)</code> and "knows" that it can't be a method invocation or variable reference because the <code>right</code> does not begin begin with a percent sign. | ||
If the compiler saw: | If the compiler saw: | ||
< | <p class="code">%transform = %right(3) | ||
%transform = %right(3) | </p> | ||
</ | |||
this might indicate a reference to an <var>Arraylist</var> called <code>%right</code>, or perhaps a local function called <code>Right</code> that, hopefully, returns a method variable. | |||
< | But, it most definitely would not be interpreted as having anything to do with the system <var>Right</var> method. | ||
function (string):right3 is longstring | But, in: | ||
<p class="code">%transform = right(3) | |||
</p> | |||
The compiler knows you must be referring to a function ''name'' and so assumes that that function is intended to be part of what's literally being assigned to the method variable. | |||
When it sees the open parenthesis after the <code>right</code>, it decides that what's inside the parentheses are intended to be arguments to be passed in the specified invocation of the method. | |||
Essentially, it is as if the compiler built a local method under the covers that looked like: | |||
<p class="code">function (string):right3 is longstring | |||
return %this:right(3) | return %this:right(3) | ||
end functions | end functions | ||
</ | </p> | ||
and then assigned that local function to %transform: | |||
< | and then assigned that local function to <code>%transform</code>: | ||
%transform = right3 | <p class="code">%transform = right3 | ||
</ | </p> | ||
In fact, to get this same functionality before | |||
By implicitly doing this for you, the compiler saves you the trouble of having to come up with a name for the method (hence the term anonymous function) and makes it clear at the point of assignment to %transform what the anonymous function actually does. | In fact, to get this same functionality before the introduction of anonymous functions, you would have to explicitly define such a local function and assign it to <code>%transform</code>. | ||
Without the anonymous function, you'd have to find the definition of Right3 to see what's actually being assigned to %transform. | By implicitly doing this for you, the compiler saves you the trouble of having to come up with a name for the method (hence the term anonymous function), and it makes it clear at the point of assignment to <code>%transform</code> what the anonymous function actually does. | ||
Without the anonymous function, you'd have to find the definition of <code>Right3</code> to see what's actually being assigned to <code>%transform</code>. | |||
This is another case where | This is another case where <var class="product">SOUL</var>'s insistence that variables begin with a percent sign comes in handy, as it allows the compiler to distinguish an anonymous function from a variable or method reference. | ||
Anonymous functions are | Anonymous functions are ''always'' considered to be '''exposed''', that is, variables in the containing context are always visible to the anonymous method. | ||
To illustrate this, consider: | To illustrate this, consider: | ||
< | <p class="code">b | ||
b | |||
%transform is function (string):transform is longstring | %transform is function (string):transform is longstring | ||
Line 481: | Line 487: | ||
end | end | ||
</ | </p> | ||
< | The output is: | ||
'We are the worms':%transform = rms | <p class="code">'We are the worms':%transform = rms | ||
'We are the worms':%transform = e worms | 'We are the worms':%transform = e worms | ||
</ | </p> | ||
Suppose, however, that you want %transform to return the | |||
One might expect the following would do | Suppose, however, that you want <code>%transform</code> to return the ''reverse'' of the last three characters in a string. | ||
< | One might expect the following would do so: | ||
%transform = right(3):reverse | <p class="code">%transform = right(3):reverse | ||
</ | </p> | ||
However, for subtle syntactic reasons, this is not allowed. | However, for subtle syntactic reasons, this is not allowed. | ||
===The This method=== | ===The This method=== | ||
To accomplish the above, you must use a special method called ''This''. | To accomplish the above, you must use a special method called '''This'''. | ||
The This method is unusual in that: | The <var>This</var> method is unusual in that: | ||
<ul> | <ul> | ||
<li>It can only be used via method variables</li> | <li>It can only be used via method variables</li> | ||
<li>The first argument is an arbitrary | |||
<li>The first argument is an arbitrary <var class="product">SOUL</var> expression that can return any datatype. | |||
The datatype returned, though, must be compatible with the return value of the method template for the method variable to which it is assigned.</li> | The datatype returned, though, must be compatible with the return value of the method template for the method variable to which it is assigned.</li> | ||
<li>The variable ''%this'' is automatically defined in the expression and holds the value of the object to which the method variable is applied.</li> | |||
<li>The variable '''%this''' is automatically defined in the expression, and it holds the value of the object to which the method variable is applied.</li> | |||
</ul> | </ul> | ||
Given these rules, the This method can be used to set a method variable to a function that takes the reverse of the rightmost three characters of a string: | |||
< | Given these rules, the <var>This</var> method can be used to set a method variable to a function that takes the reverse of the rightmost three characters of a string: | ||
%transform = this(%this:right(3):reverse) | <p class="code">%transform = this(%this:right(3):reverse) | ||
</ | </p> | ||
After the above assignment, | |||
< | After the above assignment, if you specify this: | ||
printText {~} = {'We are the worms':%transform} | <p class="code">printText {~} = {'We are the worms':%transform} | ||
</ | </p> | ||
< | The result is: | ||
'We are the worms':%transform = smr | <p class="code">'We are the worms':%transform = smr | ||
</ | </p> | ||
The This method can be useful in cases where the anonymous function needed to perform an operation that | |||
The <var>This</var> method can be useful in cases where the anonymous function is needed to perform an operation that is not a method. | |||
For example: | For example: | ||
< | <p class="code">%transform = this(%this with %this) | ||
%transform = this(%this with %this) | |||
printText {~} = {'We are the worms':%transform} | printText {~} = {'We are the worms':%transform} | ||
</ | </p> | ||
outputs | |||
< | This outputs the following: | ||
'We are the worms':%transform = We are the wormsWe are the worms | <p class="code">'We are the worms':%transform = We are the wormsWe are the worms | ||
</ | </p> | ||
< | And this: | ||
%transform = this(%this:left(1) with %this:right(1)) | <p class="code">%transform = this(%this:left(1) with %this:right(1)) | ||
printText {~} = {'We are the worms':%transform} | printText {~} = {'We are the worms':%transform} | ||
</ | </p> | ||
< | Produces this result: | ||
'We are the worms':%transform = Ws | <p class="code">'We are the worms':%transform = Ws | ||
</ | </p> | ||
As with other anonymous methods, the This method is exposed so variables in the containing context are available inside the This method | |||
< | As with other anonymous methods, the <var>This</var> method is exposed, so variables in the containing context are available inside the <var>This</var> method. Therefore, for the following: | ||
b | <p class="code">b | ||
%transform is function (string):transform is longstring | %transform is function (string):transform is longstring | ||
Line 544: | Line 554: | ||
end | end | ||
</ | </p> | ||
< | The results is: | ||
'We are the worms':%transform = W<===>s | <p class="code">'We are the worms':%transform = W<===>s | ||
</ | </p> | ||
However, since %this inside the This method refers to the method object during the method variable invocation, a %this variable in the containing context would be "hidden" | |||
This could be a problem if a This method is used inside a class instance method or an enhancement method where a %this would be defined. | However, since <code>%this</code> inside the <var>This</var> method refers to the method object during the method variable invocation, a <code>%this</code> variable in the containing context would be "hidden." | ||
For example, if | This could be a problem if a <var>This</var> method is used inside a class instance method or an enhancement method where a <code>%this</code> would be defined. | ||
< | For example, if you had: | ||
local subroutine (string):foo | <p class="code">local subroutine (string):foo | ||
%transform is function (string):transform is longstring | %transform is function (string):transform is longstring | ||
Line 561: | Line 571: | ||
end subroutine | end subroutine | ||
</ | </p> | ||
Unfortunately, that will not be the case — %this will be the method object so the above | You might actually mean for the <code>%this</code> between the two <code>Withs</code> in the <code>this</code> function to be the <code>%this</code> for the local subroutine. | ||
< | Unfortunately, that will not be the case — <code>%this</code> will be the method object, so the above <var>Subroutine</var> would output: | ||
'We are the worms':%transform = WWe are the wormss | <p class="code">'We are the worms':%transform = WWe are the wormss | ||
</ | </p> | ||
To get around this, one can either assign the method's %this to a different local variable: | |||
< | To get around this, one can either assign the method's <code>%this</code> to a different local variable: | ||
local subroutine (string):foo | <p class="code">local subroutine (string):foo | ||
%fooThis is longstring | %fooThis is longstring | ||
Line 580: | Line 590: | ||
end subroutine | end subroutine | ||
</ | </p> | ||
< | Or, map the name <code>%this</code> via an <var>InternalNames</var> block: | ||
local subroutine (string):foo | <p class="code">local subroutine (string):foo | ||
internalNames | internalNames | ||
Line 595: | Line 605: | ||
end subroutine | end subroutine | ||
</ | </p> | ||
If this | |||
< | If this <var>Subroutine</var> is called with: | ||
%x is string len 8 initial('++++') | <p class="code">%x is string len 8 initial('++++') | ||
%x:foo | %x:foo | ||
</ | </p> | ||
the | |||
< | The following is the output: | ||
'We are the worms':%transform = W++++s | <p class="code">'We are the worms':%transform = W++++s | ||
</ | </p> | ||
The This method without any parameters is equivalent to | |||
As such, the This method can be thought of having a default parameter | The <var>This</var> method without any parameters is equivalent to <code>This(%this)</code>, that is, it simply returns the method object at method variable invocation time. | ||
As such, the <var>This</var> method can be thought of as having a default parameter: <code>%this</code>. | |||
===Anonymous methods in collection methods=== | ===Anonymous methods in collection methods=== | ||
As with other method literals, anonymous methods can be useful in collection methods that use method variables to control processing. | As with other method literals, anonymous methods can be useful in [[Collections|collection]] methods that use method variables to control processing. | ||
For example, | For example, given class <code>Order</code>, with public float variables <code>price</code> and <code>quantity</code>, suppose this: | ||
<p class="code">%orders is arraylist of object order | |||
< | </p> | ||
%orders is arraylist of object order | |||
</ | You could, then, sort the <var>Arraylist</var> in descending order by quantity: | ||
<p class="code">%orders:sort(descending(quantity)) | |||
< | </p> | ||
%orders:sort(descending(quantity)) | |||
</ | To sort in descending order by the product of <code>Price</code> and <code>Quantity</code>, specify: | ||
<p class="code">%orders:sort(descending(this(%this:price * %this:quantity))) | |||
< | </p> | ||
%orders:sort(descending(this(%this:price * %this:quantity))) | |||
</ | If given this: | ||
If | <p class="code">%names is arraylist of longstring | ||
< | </p> | ||
%names is arraylist of longstring | |||
</ | The following sorts <code>%names</code> in ascending order of the second through fifth character: | ||
<p class="code">%names:sort(ascending(substring(2,4))) | |||
< | </p> | ||
%names:sort(ascending(substring(2,4))) | ==System types== | ||
</ | Some system methods expect method parameters with a particular template. Rather than requiring parameters to be described with the full function template and for variables containing functions to be defined with the templates, system types are provided as a shorthand for those signatures. For example, the [[Get (HttpRequest function)|HttpRequest Get function]] has a CertificateCheck parameter that's a function with the following template: | ||
<p class="syntax"><span class="literal">Function (String):</span><span class="term">methname</span> <span class="literal">Is Boolean</span> </p> | |||
If one wished to declare a variable with this template, one could do something like: | |||
<p class="code">%myChecker is function (string):lambda is boolean | |||
</p> | |||
However, the [[CertificateChecker type]] is provided to simplify this so one can declare a variable as: | |||
<p class="code">%myChecker is type certificateChecker | |||
</p> | |||
The two declarations are equivalent and interchangeable. In either case, a variable declared this way can be passed to the HttpRequest Get method: | |||
<p class="code">%httpResponse = %httprequest:get(certificateCheck=%myChecker) | |||
</p> | |||
==See also== | |||
<ul> | |||
<li>[[Object oriented programming in SOUL]] | |||
</ul> | |||
[[Category:SOUL object-oriented programming topics]] |
Latest revision as of 21:46, 18 December 2014
Summary
SOUL supports method variables, variables that reference methods. They can be used to invoke varying methods with a particular, unchanging invocation. For example, to conditionally invoke the system intrinsic Right, Left, or Center method, you can do something like the following:
%justify is function (string):justify(%amount is float) is longstring ... if %x then %justify = right elseif %y then %justify = centre else %justify = left end if ... printtext {'Whatever':%justify(10)} printtext {%something:%justify(10)}
As shown in the preceding example, to use a method variable you first declare it, then assign a method to it, then invoke it. The declaration specifies the type of method to which the method variable may refer. Function and Subroutine are the two type options. A Function variable is required if the method (or class variable) you want to assign to the variable returns a value (this is always true for class variables). A Subroutine variable is used if the method to be assigned does not return a value or is Callable.
The method you assign to a method variable can be a system method, a user-defined method, (instance method, enhancement method, shared method, or local method), or a class variable. Invoking the method using the method variable employs the same syntax as invoking the method directly, except that the method variable is used.
The following example shows assignment from a user class method, class variables, and a local method:
b class junk public variable a is float variable b is string len 32 function c is float end public function c is float return %this:a:toPower(3) end function end class local function (junk):aTimesC is float return %this:a * %this:c end function %rubbish is object junk %rfunc is function (junk):whatever is float %rubbish = new %rubbish:a = 3 %rubbish:b = 'Holy cow!' %rfunc = a printText {~} = {%rubbish:%rfunc:right(20, pad='*')} %rfunc = b printText {~} = {%rubbish:%rfunc} %rfunc = c printText {~} = {%rubbish:%rfunc} %rfunc = aTimesC printText {~} = {%rubbish:%rfunc} end
The result is:
%rubbish:%rfunc:right(20, pad='*') = *******************3 %rubbish:%rfunc = Holy cow! %rubbish:%rfunc = 27 %rubbish:%rfunc = 81
Note: The second line of the result contains string output. Because of loose datatyping for intrinsics, a method variable declared as Float can return a String (or Unicode) value, if the method assigned to it returns such a value.
Notice also that class Variables are used exactly like the Functions in the examples above.
Function or Subroutine variables may not be passed between requests; they are always nullified between requests. Function or Subroutine variables may not appear inside of global objects.
Declaring a method variable
The syntax for the declaration of a method variable is shown below:
%var [Is] methodTemplate | (methodTemplate) [methvarQualifiers]
Where:
If there are any method variable qualifiers, methodTemplate must be enclosed in parentheses. For example if a method variable is Shared, that qualifier must be specified as in:
%x is (function (stringlist):stupid is float) shared
%var | The name of the method variable, which can be any name that follows the rules for SOUL %variables. |
---|---|
methodTemplate | The function or subroutine declaration template. |
methvarQualifiers | Qualifiers for the variable being declared, such as Common or Shared. |
Method template in method variable declaration
A method declaration template is used to declare the type of a method variable and is essentially the same as a normal method declaration, consisting of the method name followed by the method description.
methodType [(class):]methodName[(parms)] - [Is returnType] [methodQualifiers]
methodType | The type of method that is to be represented by the method variable.
The options are Function and Subroutine. Function is required if the method (or class variable) returns a value. Subroutine is used if the method does not return a value or is Callable. Strictly speaking, Function and Subroutine are the classes of the method object that is is referenced by the method variable. There are currently no methods in the Function or Subroutine class. |
---|---|
class | Identifies the class of objects to which the method applies. |
methodName | The method name, which can be any name that follows the rules for SOUL %variables, is preceded by its class as necessary, and is followed by its parameters, if any. |
parms | The parameters that are used in invoking the method variable. |
returnType | The datatype of values returned by the methods assigned to the method variable. This is only allowed for Function class method variables. |
methodQualifiers | Details of method operation. Since the method operation details tend to come from the method to which the variable is set, methodQualifiers is rarely used. |
Method class in method variable method template
Two method variables that are identically declared except for their class specifications are not equivalent. That is, given these declarations:
%foo is function (stringlist):hash is float %bar is function (stringlist):hash is float %junk is function (xmlDoc):hash is float
The following is valid:
%foo = %bar
But the following is not valid:
%foo = %junk
The class designation supplies context for any method assigned to the method variable. Given the declarations in the previous item, the following assignment implies the Count method in the Stringlist class:
%foo = count
Count
above references the Stringlist class Count method unless you had created a local Stringlist enhancement method called "Count," in which case method %foo
references the local Count
method.
Omitting a class specification implies that the method referred to by the method variable does not operate on an instance of an object variable, that is, it is a shared method. Such a method variable may be assigned to a conforming shared method in any class.
For example, the following declaration specifies a method variable that does not apply to an object instance:
%debug is subroutine debug
Consequently, valid assignments could take any of the following forms:
%debug = (myclass):sharedSubroutine %debug = (yourclass):sharedSubroutine %debug = localMethod %debug = (object):garbageCollect
Method parameter list and other qualifiers in method variable method template
The method parameter list can contain required and optional, named and unnamed parameters, and their types. The parameter list is followed by a method result datatype (for functions) and then possibly by further qualifiers.
For example, a Calculate
method in Myclass
that takes a numeric value
and returns a numeric value is declared as:
function (myclass):calculate(%x is float) is float
A method variable declared with a return type that is a SOUL intrinsic datatype might return another intrinsic type when invoked if you assign a method to it that returns the other type. This is because SOUL allows the assignment from one intrinsic type to another.
Most method qualifiers such as AllowNullObject or Implements are derived from the method assigned to a method variable and make no sense on the method template in a method variable declaration. In fact, there are currently no method qualifiers other than the return datatype that are allowed in the method template in a method variable declaration. This includes Throws clauses, though you may assign a method that throws exceptions to a method variable and catch exceptions thrown by that method.
Method variable declaration example
In the following example, the %stooge
Function variable is declared for the Mayhem
class:
class mayhem public variable moe is float property larry is float function curly is float end public end class %stooge is function (mayhem):Number is float
In this declaration, function
is used for the object declaration, indicating that the method returns a value. The name Number
is actually nothing but a placeholder, and the above declaration is considered identical to:
%stooge is function (mayhem):Placehold is float
Some placeholder is needed in the Function name position to make the declaration read properly, and you may want to put a meaningful name there to suggest the intent of the method.
But whether the name is specified as Number
or as Placehold
, the following assignments are all valid:
%stooge = moe %stooge = larry %stooge = curly
The method values to which the method variable in this example refers are in fact class Variables, which fit the %stooge declaration: they are function-like, operating on a Mayhem
object and returning a Float.
If the method in the declaration has parameters, as in:
%stooge is function (mayhem):meaningless(%x is float) is float
The parameter names do not get used, so the above declaration is equivalent to this one:
%stooge is function (mayhem):meaningful(%y is float) is float
Thus, if two method variables contain references to methods on a Mayhem
object that take a float input parameter and return a float, they can be assigned to each other, as in the following:
%stooge is function (mayhem):meaningless(%x is float) is float %stud is function (mayhem):meaningful(%y is float)is float ... %stooge = %stud
For NameAllowed or NameRequired parameters, the parameter names are meaningful in the method object declarations.
Assigning to a method variable
You assign a method value to a method variable using the following syntax:
%var = [(+classname)]method
Where:
%var | The name of the method variable. |
---|---|
classname | The name of the class to which the method variable applies.
This name is necesary if the method is a shared method or an enhancement method; otherwise, the method is assumed to be a member of the class specified in the method variable declaration. The plus sign is necessary for an enhancement method. |
method | The name of the method to which the method variable will refer or an anonymous function. A method name can be either a SOUL method (instance method, enhancement method, shared method, or local method), a class member (Variable or read-only Property), or a method variable that fits the #method template in method variable declaration. |
The source of an assignment to a method variable can even be the invocation of a function that returns a method value that fits the method description. Of course, it can also be another, compatible method variable.
You may assign a method that takes no parameters to a method variable that is declared as referring to a method with a parameter.
For example, if function variable %goo
is declared as:
%whynot is function (silly):whatever(%x is float) is float
The following assignment is valid if Z
is a variable in class Silly
:
%whynot = Z
In such a case, the parameter, %x
, in the method variable declaration is ignored when the method variable is invoked.
For example, if %whynot
was set to Silly
class variable Z
, the 22
in the following invocation of %whynot
would be ignored:
printText {~} = {%sillyVar:%whynot(22)}
This is allowed because methods are allowed to ignore a parameter value.
Similarly, say a function in class Silly
is defined as:
function stupid(%whatever is float, - %z is longstring optional) is longstring
Then Stupid
may also be validly assigned to %whynot
:
%whynot = stupid
And whenever Stupid
is invoked via %whynot
, the optional parameter %z
will not be present.
Method variables follow the standard rules for NameAllowed and NameRequired parameters. If a method variable declaration specifies a method with one of these types of parameter, you may not assign to the variable a method that does not have the same parameter name type.
A method variable may be assigned to an enhancement method.
The following statement assigns to %whynot
the Float enhancement method in class Util
called BigOnes
which returns a Float object:
%whynot = (+util)bigOnes
If a method variable is declared as applying to a base class, you may not assign an extension class method to it. The method variable must be applicable to all objects of the base class, and setting it to a method of a specific extension class would violate this principle.
You may assign an overridable method to a method variable.
Method variables may be assigned to the special value This, which is an identity method that simply returns its method object.
For example, given the following code fragment:
%justify is function (string):justify(%amount is float) is longstring ... if %x then %justify = right elseif %y then %justify = centre elseif %z %justify = left else %justify = this end if ... printtext {'Whatever':%justify(10)} printtext {%something:%justify(10)}
If the Else
logical path is followed, the printText
statement will simply print the string to which it is applied, without justification or padding.
The This value is especially useful for sorting and finding maximum and minimum values in collections of intrinsic objects. This can also be used as an anonymous function where the anonymous function specifies processing to be applied to the method object.
Invoking a method variable
The syntax for invoking a method via a method variable that applies to objects of a class is:
objectVar:%methodVar[:moreMethods]
The syntax for invoking a method via a method variable that doesn't apply to objects of a class is:
%(Local):%methodVar[:moreMethods]
Where:
objectVar | The object variable to which the method variable, %methodVar, is applied. |
---|---|
%methodVar | The name of the method variable. |
moreMethods | Additional methods that may be strung together, that is, applied to the result of the %methodVar method. This is no different from stringing a method to the result of a non-variable method. |
Method variable invocation examples
In the following example, method variable %foo
has this declaration:
%foo is function (stringlist):subset is object stringlist
This declaration indicates that %foo
must contain a reference to a method that operates on a Stringlist object and returns a Stringlist object.
The following local function is defined in the same scope as %foo
, after which the function is assigned to %foo
:
local function (stringlist):everyOther is object stringlist %outlist is object stringlist %i is float %outlist = new for %i from 1 to %this:count by 2 %outlist:add(%this(%i)) end for return %outlist end function ... %foo = everyOther
To invoke the EveryOther
method against Stringlist variable %list
, you apply method variable %foo
:
%list = %list:%foo
To invoke the EveryOther method against %list
and then print the result, you use:
%list:%foo:print
Stringing methods together can be taken further.
To invoke EveryOther
on %list
, then apply EveryOther
to the result of
that, and finally print the output of those operations:
%list:%foo:%foo:print
In the following example, a method variable that is set to a Shared class variable is invoked using %(local)
, the syntax for invoking a local shared method.
The method variable is not applied to a variable because the method, itself, doesn't apply to objects of any class.
begin class Reply public shared variable validate is float initial(1) end public shared end class Reply %rc is float %rcMeth is function rcReturner is float %rcMeth = (Reply):validate %rc = %(local):%rcmeth if %rc then printText Error else printText Success end if end
This request prints: Error
.
Anonymous functions
An anonymous function, as the name suggests, is a method that has no name. Because these functions have no name, they must typically be defined in the context in which they are actually used. Hopefully, the meaning of this statement is made clear below.
Consider the case where a method variable is defined as:
%transform is function (string):transform is longstring
So, %transform
can refer to a method that takes a String input and produces a String output.
So, one can set %transform
to the system Reverse function:
%transform = reverse
And then invoke the Reverse function via the method variable:
printText {~} = {'We are the worms':%transform}
which produces:
'We are the worms':%transform = smrow eht era eW
But, suppose you wanted %transform
to be set to a method that returns the rightmost three characters of the method object string.
Intuitively, it would seem that you should be able to do:
%transform = right(3)
But, formerly, this was not allowed because there is no method called Right(3)
, only Right
. And, one couldn't do:
%transform = right
because right
requires a parameter, but the method variable template for %transform
has no parameters.
And, even if the above were allowed, it is still missing the important bit of information about how many of the rightmost bytes %transform
should return.
Now, however, this is allowed:
%transform = right(3)
It is worth understanding exactly what this means.
The compiler sees right(3)
and "knows" that it can't be a method invocation or variable reference because the right
does not begin begin with a percent sign.
If the compiler saw:
%transform = %right(3)
this might indicate a reference to an Arraylist called %right
, or perhaps a local function called Right
that, hopefully, returns a method variable.
But, it most definitely would not be interpreted as having anything to do with the system Right method.
But, in:
%transform = right(3)
The compiler knows you must be referring to a function name and so assumes that that function is intended to be part of what's literally being assigned to the method variable.
When it sees the open parenthesis after the right
, it decides that what's inside the parentheses are intended to be arguments to be passed in the specified invocation of the method.
Essentially, it is as if the compiler built a local method under the covers that looked like:
function (string):right3 is longstring return %this:right(3) end functions
and then assigned that local function to %transform
:
%transform = right3
In fact, to get this same functionality before the introduction of anonymous functions, you would have to explicitly define such a local function and assign it to %transform
.
By implicitly doing this for you, the compiler saves you the trouble of having to come up with a name for the method (hence the term anonymous function), and it makes it clear at the point of assignment to %transform
what the anonymous function actually does.
Without the anonymous function, you'd have to find the definition of Right3
to see what's actually being assigned to %transform
.
This is another case where SOUL's insistence that variables begin with a percent sign comes in handy, as it allows the compiler to distinguish an anonymous function from a variable or method reference.
Anonymous functions are always considered to be exposed, that is, variables in the containing context are always visible to the anonymous method. To illustrate this, consider:
b %transform is function (string):transform is longstring %nright is float %transform = right(%nright) %nright = 3 printText {~} = {'We are the worms':%transform} %nright = 7 printText {~} = {'We are the worms':%transform} end
The output is:
'We are the worms':%transform = rms 'We are the worms':%transform = e worms
Suppose, however, that you want %transform
to return the reverse of the last three characters in a string.
One might expect the following would do so:
%transform = right(3):reverse
However, for subtle syntactic reasons, this is not allowed.
The This method
To accomplish the above, you must use a special method called This. The This method is unusual in that:
- It can only be used via method variables
- The first argument is an arbitrary SOUL expression that can return any datatype. The datatype returned, though, must be compatible with the return value of the method template for the method variable to which it is assigned.
- The variable %this is automatically defined in the expression, and it holds the value of the object to which the method variable is applied.
Given these rules, the This method can be used to set a method variable to a function that takes the reverse of the rightmost three characters of a string:
%transform = this(%this:right(3):reverse)
After the above assignment, if you specify this:
printText {~} = {'We are the worms':%transform}
The result is:
'We are the worms':%transform = smr
The This method can be useful in cases where the anonymous function is needed to perform an operation that is not a method. For example:
%transform = this(%this with %this) printText {~} = {'We are the worms':%transform}
This outputs the following:
'We are the worms':%transform = We are the wormsWe are the worms
And this:
%transform = this(%this:left(1) with %this:right(1)) printText {~} = {'We are the worms':%transform}
Produces this result:
'We are the worms':%transform = Ws
As with other anonymous methods, the This method is exposed, so variables in the containing context are available inside the This method. Therefore, for the following:
b %transform is function (string):transform is longstring %middle is string len 8 initial('<===>') %transform = this(%this:left(1) with %middle with %this:right(1)) printText {~} = {'We are the worms':%transform} end
The results is:
'We are the worms':%transform = W<===>s
However, since %this
inside the This method refers to the method object during the method variable invocation, a %this
variable in the containing context would be "hidden."
This could be a problem if a This method is used inside a class instance method or an enhancement method where a %this
would be defined.
For example, if you had:
local subroutine (string):foo %transform is function (string):transform is longstring %transform = this(%this:left(1) with %this with %this:right(1)) printText {~} = {'We are the worms':%transform} end subroutine
You might actually mean for the %this
between the two Withs
in the this
function to be the %this
for the local subroutine.
Unfortunately, that will not be the case — %this
will be the method object, so the above Subroutine would output:
'We are the worms':%transform = WWe are the wormss
To get around this, one can either assign the method's %this
to a different local variable:
local subroutine (string):foo %fooThis is longstring %fooThis = %this %transform is function (string):transform is longstring %transform = this(%this:left(1) with %fooThis with %this:right(1)) printText {~} = {'We are the worms':%transform} end subroutine
Or, map the name %this
via an InternalNames block:
local subroutine (string):foo internalNames %fooThis is %this end internalNames %transform is function (string):transform is longstring %transform = this(%this:left(1) with %fooThis with %this:right(1)) printText {~} = {'We are the worms':%transform} end subroutine
If this Subroutine is called with:
%x is string len 8 initial('++++') %x:foo
The following is the output:
'We are the worms':%transform = W++++s
The This method without any parameters is equivalent to This(%this)
, that is, it simply returns the method object at method variable invocation time.
As such, the This method can be thought of as having a default parameter: %this
.
Anonymous methods in collection methods
As with other method literals, anonymous methods can be useful in collection methods that use method variables to control processing.
For example, given class Order
, with public float variables price
and quantity
, suppose this:
%orders is arraylist of object order
You could, then, sort the Arraylist in descending order by quantity:
%orders:sort(descending(quantity))
To sort in descending order by the product of Price
and Quantity
, specify:
%orders:sort(descending(this(%this:price * %this:quantity)))
If given this:
%names is arraylist of longstring
The following sorts %names
in ascending order of the second through fifth character:
%names:sort(ascending(substring(2,4)))
System types
Some system methods expect method parameters with a particular template. Rather than requiring parameters to be described with the full function template and for variables containing functions to be defined with the templates, system types are provided as a shorthand for those signatures. For example, the HttpRequest Get function has a CertificateCheck parameter that's a function with the following template:
Function (String):methname Is Boolean
If one wished to declare a variable with this template, one could do something like:
%myChecker is function (string):lambda is boolean
However, the CertificateChecker type is provided to simplify this so one can declare a variable as:
%myChecker is type certificateChecker
The two declarations are equivalent and interchangeable. In either case, a variable declared this way can be passed to the HttpRequest Get method:
%httpResponse = %httprequest:get(certificateCheck=%myChecker)