Methods
Methods
Methods include functions (which return a value) and subroutines (which do not). Declare a subroutine with this syntax:
[Shared] <Scope> Sub <Name>( <parameters> )
...
End Sub
|
To make a function, use this syntax:
[Shared] <Scope> Function <Name>( <parameters> ) As <type>
...
End Function
|
<Scope> is one of "Private", "Protected", or "Public" (or you may leave the scope blank, and "public" is assumed). Parameters is a comma-separated list of parameter declarations, each of the form:
[<modifier>] <paramName> As <type> [ = <defaultValue> ]
|
The <modifier> in a parameter declaration is one of "ByRef" or "ByVal", and for the very last parameter in the list, may also be "Assigns". If no modifier is given, the "ByVal" is assumed.
<defaultValue> must be a literal constant, such as 42 or "orbiting brain lasers".
<type> may be any type declaration, such as the name of a class or one of the built-in types ("Integer", "String", "Boolean", etc.).
ScopeEvery method has a scope which controls what other code is able to access it, and how. The exact meaning of the scope depends on whether the method is in a class or a module.
A module method declared "Public" may be accessed from anywhere in the program, using only its name. This is generally considered bad form, since if you had two such methods using the same name, even if they were in different modules, it could be ambiguous which one was meant.
A module method declared "Protected" may also be accessed from anywhere in the program, but any code outside that module must prefix the method name with the module name. So for example, if a module called "Utils" has a protected method called "Rehash", then code outside the Utils module would invoke this method as "Utils.Rehash". Code inside the same module can access the method with or without a prefix.
A module method declared "Private" can only be accessed within that method; it is completely invisible (and unavailable) to code elsewhere in the program. Code within that same module can access the method with or without a prefix.
For class methods, the situation is similar, except that a prefix is always required outside the class, and we must consider code inside subclasses as well. A Public class method can be accessed from anywhere in the program, but must always be prefixed with an instance of that class. For example, if class "Fruit" has a public method called "Squish", and "aGrape" refers to a Fruit instance, then we would squish it by calling "aGrape.Squish". Code inside the Fruit class, or its subclasses, may call it without a prefix (in which case the prefix "self." is assumed).
A class method declared "Protected" is invoked with the very same syntax, but may only be called from within the same class or its subclasses. Continuing our example above, "aGrape.Squish" would only be a valid call within the Fruit class or any of its subclasses.
A class method declared "Private" is accessible only within the class containing that method; it is unavailable even to subclasses.
Shared MethodsMost class methods are technically instance methods — they refer to a particular instance of a class, and can access any of the properties or methods of that instance either implicitly or via the "self." prefix. However, it is also possible to make a shared class method — one that refers not to any particular instance of the class, but is shared among all instances. This is done by adding the "Shared" keyword to the start of the method declaration.
A shared method has no "self" defined, and has no implicit access to any non-shared methods or properties of the class. However, if it obtains a reference to a class instance by some other means, then it can access all the protected and private methods and properties of that class, just like an instance method could.
A common application of shared methods is to control instantiation of a class, by making the Constructor protected or private, so that no code outside the class can instantiate it; and then adding a public shared function that creates an instance (perhaps making sure that certain conditions are met) and returns it to the caller. This is an easy way to implement the Singleton design pattern, for example.
AssignsThe last parameter in a subroutine declaration may be modified with the "Assigns" keyword. This changes the calling syntax, such that the last argument is passed to the right-hand of an assignment ("=") operator. In other words, it makes a method that you invoke by assigning a value to it. As an example, consider the following:
Sub Value(key As String, newValue As Integer)
This would be invoked in the usual way:
Value "answer", 42
However, by using the Assigns keyword:
Sub Value(key As String, Assigns newValue As Integer)
you would now invoke it as an assignment:
Value( "answer" ) = 42
The Assigns feature is just syntactic sugar, and does not change any of the calling semantics (such as scope protection).
Parentheses Around Method ArgumentsUnlike most languages, Yuma requires parentheses only where they are needed to separate arguments from the rest of an expression. They're never needed for methods with no parameters, since in this case, the compiler knows not to expect any arguments. They're also not needed for subroutines, since a subroutine call can't be part of an expression. They are only needed for function calls that take arguments. Examples:
aGrape.Squish
x = Rnd - 0.5
ans = Val( "6" ) * 7
Note that of the three method calls (Squish, Rnd, and Val) in this example, only Val needs parentheses, because it is the only function call with any arguments.