Methods
method <name>(<parameters>) {
<statements>
}
method <name>(<parameters>):<type> {
<statements>
}
function <name>(<parameters>):<type> {
<statements>
}
init(<parameters>) {
<statements>
}
A method is a named block of code which can be invoked by calling the method.
Methods take zero or more parameters, which are values passed in to the method that it can
reference as if they were local variables. Each parameter is a name, a colon (:
), and a
type, and multiple parameters are separated by a comma (,
). Methods may return a
value, which becomes the value of the method call expression.
Example:
method sayHello(name:String) {
Console.printLine("Hello, \{name}!")
}
This defines a method named sayHello
, which takes a single String
argument named name
. When
called (such as by sayHello("Ethan")
), the statements within the method will be executed, in this
case invoking the Console.printLine
method to display the text Hello, Ethan!
.
To return a value from a method, first give the method a return type and then use return <value>
from within the method:
method square(x:Real):Real {
return x * x
}
This defines a method which returns the value of its argument squared.
Annotations
The following annotations are legal on methods:
@private
@protected
@class
@abstract
@extendable
@override
@pre(...)
@preOr(...)
@post(...)
@postAnd(...)
Variable Parameters
By default, method parameters behave as defines; that is, the object the method parameter refers to may be modified, but the parameter may not be reassigned to point to a different object.
method example(param:MutableString) {
param.append("this works!") -- legal!
param := MutableString("this doesn't!") -- illegal, won't compile
}
)
The var
keyword turns a method parameter into an ordinary variable, which may then be freely
modified:
method countDown(var i:Int) {
while i > 0 {
Console.printLine(i)
i -= 1
}
}
Variable method parameters are treated as ordinary local variables, and reassigning the variable does not affect anything outside of the method itself.
self
Within an instance method (including functions and inits), you
may refer to the object on which the method is running using the self
keyword.
Overriding Methods
Methods present in a superclass may be overridden by methods with the same signature present in a subclass. For instance, given:
@extendable
class Super {
@extendable
method performFoo() {
Console.printLine("superclass method!")
}
}
You may provide a different implementation of the method in a subclass:
class Sub : Super {
@override
method performFoo() {
Console.printLine("subclass method!")
}
}
)
The fact that the subclass method overrides the superclass method means it will be called instead of
its superclass equivalent when the object is of type Sub
. The required @override
annotation
prevents you from accidentally overriding superclass methods without realizing it.
Whenever the performFoo
method is called, the right implementation of the method will be selected
at runtime based on the class of the object:
var object := Super()
object.performFoo() -- displays "superclass method!"
object := Sub()
object.performFoo() -- displays "subclass method!"
All parameter types and the return type of an override method must match exactly (including nullability).
Calling superclass methods
When you have overridden a method, you may find yourself needing to call the superclass' version of the method. For instance,
@extendable
class Text {
@extendable
method paint() {
...
}
}
class UnderlinedText : Text {
@override
method paint() {
...
}
}
)
We would like to have UnderlinedText
do whatever drawing Text
does, and then
draw a line under the text. We can do this with the syntax super.paint()
:
class UnderlinedText : Text {
method drawUnderline() {
...
}
@override
method paint() {
super.paint()
drawUnderline()
}
}
)
The syntax super.paint()
means "call the version of the method present in my superclass", in this
case Text.paint()
.
Method Values
Methods and functions are first-class values in Frost, meaning that they can be stored in variables, passed into and returned from methods, and otherwise be used as any other value would be. There are three ways to use a method as a value:
Inline methods, such as:
list.apply(method(s:String) {
...
})
Lambdas, such as:
list.fold((x, y) => x.union(y))
)
Method References, such as:
list.fold(Int.+)