Functions & Subroutines

Thanks to its VBScript heritage, Wasabi makes a distinction between functions and subroutines (Wikipedia, however, does not): functions return a value while subroutines do not. This ostensibly provides a distinction between functions you call for the value they return (functions) and ones you call for the side effects they cause (subroutine).

Declaring Functions

Functions are defined using the Function keyword and values are returned using the Return statement:

Function Fib(n)
  If n < 2 Then
    Return 1
  End If

  Return Fib(n - 1) + Fib(n - 2)
End Function

Warning

Inherited from VBScript, Wasabi allows you to set the return value of a Function by assigning to the function’s name and to exit early using the Exit Function statement:

Function Fib(n)
  If n < 2 Then
    Fib = 1
    Exit Function
  End If

  Fib = Fib(n - 1) + Fib(n - 2)
End Function

These practices are deprecated. Use the Return instead.

Optional and Named Parameters

Functions allow you to specify optional parameters using the Optional:

Function Indent(node, Optional depth = 0)
  Dim indent = "".PadLeft(depth)
  %><%= indent %><%= node %><%

  For Each Dim child in node.Children()
    Indent(child, depth + 1)
        Next
End Function

Indent(tree)

Futhermore, parameters can be specified by name using the := syntax:

Indent(node := tree, depth := 10)

This is particularly useful for destinguishing literal parameters of the same type, but is also commonly used in conjunction with Optional parameters. Keep in mind that all positional parameters must come before named parameters.

Type Annotations

While Wasabi’s type inferencer will infer the types of the majority of variables, you can provide type annotations to explicitly identify a functions return type and the types of it’s parameters:

Function Fib(n As Int32) As Int32
        ' Calculate Fib(n)
End Function

Generally, it is perfectly acceptable to not include type annotations on internal variables, but it is generally a good idea to provide type annotations on your external interfaces so that:

  1. The types accepted by your interfaces don’t change inadvertently.
  2. You don’t end up with a bunch of nonsensical type errors when you start calling something differently.

Shared Values

Note

CRequestCache.was provides a per-request cache and it is generally perferable to use that rather than extensively using Shared values.

In side of functions and subroutines, Wasabi allows you to declare Shared values that are simpliar to C-style static variables but whose lifespan is limited to the current request. This behaviour can be used to implment a simple per-request cache:

Function RequestCache()
        Dim Shared cache = New [System.Collections.Hashtable]()
        Return cache
End Function

In the above code, the first line is only executed once per request. Consequently, every invocation of RequestCache() in a given request will retrieve the same dictionary which allows the callers to to share a single cached object.

Declaring Subroutines

Subroutines are defined using the Sub keyword:

Sub Greetings(name, adjective)
  %>Hello <%= name %>. It's a <%= adjective %> day!<%
End Sub

... and invoked like so:

Greetings "Aaron", "wonderful"

Notice that the arguments to the subroutine call are not surrounded in parenthesis. The grammar does allow for parens to be included for single argument calls, but this is simply an artifact of using parens to group expressions and not part of the grammar of subroutine calls. Early exit from a subroutine is accomplished using the Return statement, for example:

Sub Example(predicate)
  If predicate Then
    Return
  End If

  ' Do stuff
End Sub

Warning

Wasabi also allows a subrouting to exit early using the Exit Sub statement. This statment is deprecated, use Return instead.

Function Objects

Note

Wasabi functions are not function objects, and do not capture their enclosing scope. In order to pass a function defined in a Function statement to a higher-order function you must wrap it in a simple Lambda.

Wasabi supports function objects; however, not all functions are objects, they must be created explicitly using the Lambda keyword. There are two forms of the Lambda expression a short form:

Dim predicate = Lambda (x) x > 0

... and a long form:

Function MakeAdder(x)
  Return Lambda (y)
    Return x + y
  End Lambda
End Function

' Returns: 3
MakeAdder(1)(2)

Lambda objects do capture the objects they reference in their enclosing scope, making them closures.

Type Annotations

The type of a function object is declared using the Function keyword:

Function Filter(predicate As Function(Variant, Returns Boolean), list As Variant())
        ...
End Function

References to Named Functions

The GetRef(name) built-in function returns a reference to a named function that can be passed around like a Lambda object.