Sage
Tusk Language
Welcome to Sage
Volume (61%) Hide Volume
Topics
Functions
Tusk programs have access to a vast collection of routines (procedures and function) defined in Delphi, including…
  • Delphi intrinsic routines such as Abs and Ord,
  • Standard Delphi RTL routines such as Pos and StrToInt,
  • Familiar in-house library routines such as HasText and DSFormat,
  • Application-specific routines, exposed by the host program.

For a complete guide to the first three categories, consult the Tusk Runtime Library volume.

Of course, new routines may also be written in Tusk itself, for example…

function SumOfCubes(const Data: TArray<Double>): Double; begin Result := 0; for var x in Data do Result := Result + x**3; end;

Subsequently, the above function may be called like so…

Writeln(SumOfCubes([1,2,3,4,5])); // prints 225

For more details on calling routines, consult the Function Calls article.

Trailing Semi-Colons

Tusk allows a trailing semi-colon when defining a routine's parameters…

procedure Work( x: Integer = 0; y: Integer = 1; z: Integer = 2; ); begin Writeln(x); Writeln(y); Writeln(z); end;

The Result Variable

In Tusk, a function has an automatically defined variable named Result, which holds the function's result. This variable is always zero-initialized (it never holds a "garbage" value).

Procedures do not have access to Result, except from an enclosing scope…

function IntToStr(x: Integer): string; procedure HandleNegative; begin Result := '-' + Result; end; var i: Integer; begin Result := ''; i := Abs(x); while i > 0 do begin Result := Chr(Ord('0') + i mod 10) + Result; i := i div 10; end; if x < 0 then HandleNegative; end;

A function cannot have a parameter or local variable or constant named Result (this restriction doesn't apply to inline variables and constants).

The Exit Statement

The other way to specify a function's return value is with the Exit statement…

function IsPrime(n: Integer): Boolean; begin if (n < 2) or (n mod 2 = 0) then Exit(False); if n < 4 then Exit(True); for var i := 3 to n div 2 do if n mod i = 0 then Exit(False); Result := True; end;

Exit may be used without an expression, in which case the function terminates early, returning whatever value is currently in Result

function CalculateShippingCost( Weight: Double; Distance: Integer): Double; begin Result := 5.00; // Base shipping cost if Weight <= 0 then Exit; // Return base cost for invalid weight if Distance <= 0 then Exit; // Return base cost for invalid distance // Calculate actual cost based on weight and distance Result := Result + (Weight * 0.50) + (Distance * 0.10); end;

The Exit statement may also be used in procedures, in which case the argument must be omitted…

procedure LogError(const Msg: string); begin if Msg = '' then Exit; // Nothing to log Writeln('[ERROR] ', Msg); AppendToFile('error.log', Msg); end;

Function Overloading

Tusk allows multiple versions of a routine with the same name, but different numbers and/or types of parameters. For example…

procedure Emit(const Value: string); overload; begin Writeln(QuoteStr(Value, '''')); end; procedure Emit(Value: TDateTime); overload; begin Writeln('#(', DateTimeToStr(Value), ')'); end;

Above, Emit may be called with a string or with a TDateTime. Note the presence of the overload directive. As in Delphi, this engages the overloading mechanism.

Additional details on overloading can be found here.

Forward Declarations

Tusk allows a routine to be declared and called before its definition, allowing for mutually recursive routines. For example…

function ParseFactor( const S: string; var Pos: Integer): Double; forward; function ParseExpr( const S: string; var Pos: Integer): Double; begin Result := ParseFactor(S, Pos); // Skip whitespace while (Pos <= Length(S)) and (S[Pos] in dscsWhiteSpace) do Inc(Pos); // Handle addition while (Pos <= Length(S)) and (S[Pos] = '+') do begin Inc(Pos); // Skip '+' Result := Result + ParseFactor(S, Pos); while (Pos <= Length(S)) and (S[Pos] = ' ') do Inc(Pos); end; end; function ParseFactor( const S: string; var Pos: Integer): Double; const DigitOrDot = dscsDigit + '.'; var StartPos: Integer; NumStr: string; begin // Skip whitespace while (Pos <= Length(S)) and (S[Pos] in dscsWhitespace) do Inc(Pos); // Handle parenthesized expression if S[Pos] = '(' then begin Inc(Pos); // Skip '(' Result := ParseExpr(S, Pos); // Recurse to expression parser Inc(Pos); // Skip ')' end else begin // Parse number StartPos := Pos; while (Pos <= Length(S)) and (S[Pos] in DigitOrDot) do Inc(Pos); NumStr := Copy(S, StartPos, Pos - StartPos); Result := StrToFloat(NumStr); end; end; function EvalExpr(const S: string): Double; begin var Pos := 1; Result := ParseExpr(S, Pos); end; Writeln(EvalExpr('5+3')); // prints 8 Writeln(EvalExpr('(10+5) + 3')); // prints 18

Above, ParseFactor and ParseExpr each calls the other, so one must be forward declared (in this case, ParseFactor).

Warning

Tusk does not currently allow using both the forward and overload directives on the same routine. This will be fixed in the near future.


One-Liner Routines

Tusk offers slightly more concise notation for defining short functions and procedures…

// Traditional approach function Sum(x, y: Integer): Integer; begin Result := x + y; end; // More concise alternative function Sum(x, y: Integer): Integer = x + y; // Traditional approach procedure Display(a, b: Integer); begin Writeln(a, ' + ', b, ' = ', Sum(a, b)); end; // More concise alternative procedure Display(a, b: Integer) = Writeln(a, ' + ', b, ' = ', Sum(a, b));

For a one-line procedure, what follows the = sign is a single statement.

For a one-line function, what follows the = sign is a single expression.

Note

The Result variable is not defined in one-line functions.


Inferred Return Type

Tusk supports type inference for the return type of a function when using the one-line approach…

// Implicitly returns Integer function Sum(x, y: Integer) = x + y;

Note that this feature is not compatible with recursive functions. So, instead of this…

function f(x: Integer) = if x=0 then 0 else x+f(x-1);

you'll need to do this…

function f(x: Integer): Integer = if x=0 then 0 else x+f(x-1);

Anonymous Methods

Like Delphi, Tusk supports anonymous methods (routines defined "inline", as an expression, often passed as an argument to another routine). For example…

var ls: IStringList := NewStringList; ls.Add('apple'); ls.Add('banana'); ls.Add('cherry'); ls.Add('date'); ls.Add('fig'); ls.Add('grape'); // Prints: apple,banana,cherry,date,fig,grape Writeln(ls.CommaText); ls.DeleteWhere( function(const s: string): Boolean begin Result := Length(s) <= 4; end); // Prints: apple,banana,cherry,grape Writeln(ls.CommaText);

Above, the call to DeleteWhere passes an anonymous method.

Anonymous methods are compatible with the one-liner notation, so the above call to DeleteWhere could be shortened to…

ls.DeleteWhere( function(const s: string) = Length(s) <= 4);

The keyword nil is assignment-compatible with all anonymous method types.

Important

As in Delphi, Tusk captures variables from enclosing scopes by reference, not by value.


Last Modified: 2/15 10:06:11 am
In this article (top)  View article's Sage markup
2/15 10:06:11 am