Esta página aún no está traducida al español. Leer la versión en inglés

Functions

Antril functions and methods - declarations, parameters, bodies, overloads and how a call is resolved, function values and func types, lambdas and what they capture, and extension blocks.

Última actualización: 9 min de lectura Editar en GitHub

En esta página

Declaring a function

[attributes] [modifiers] Name[<type parameters>](parameters) [: Result] [into namespace N] body
AntrilRun
import System;

// A block body.
Gcd(i64 a, i64 b) : i64
{
	loop (b != 0)
	{
		i64 t = a % b;
		a = b;
		b = t;
	}
	return a;
}

// An expression body.
Square(i32 x) : i32 => x * x;

// No result: the function returns nothing.
Report(str label, i64 value)
{
	Console.WriteLine(label + ": " + value);
}

Main()
{
	Report("gcd", Gcd(84, 36));
	Report("square", Square(9));
}
Terminal
larva run
gcd: 12
square: 81
  • The name comes first and the result type last, after :. Writing the result type first (i32 Square(…)) is error AN0020.
  • A function declared in a namespace is a free function; one declared in a type is a method (see Members). Both are written the same way.
  • Every path of a function with a result must end in return value; or throw. return; ends a function without one (AN2001, AN2008, AN2009).
  • An expression body, => expression;, returns the expression's value (or just evaluates it when there is no result).
  • A function without a body ends in ;. Only bodiless methods of a base class or an interface, and [Import] functions, can leave the body out (AN1027).
  • Functions cannot be declared inside another function's body; use a lambda there.
Modifier Meaning
static A method of the type, not of an object.
unsafe The body may use pointers (see Memory).
async The function returns a task and may await (see Iterators and async).
virtual, override, final Overriding, for methods (see Members).
access words See Access.

Parameters

Each parameter is a type and a name, separated by commas: (str label, i64 value). Two parameters cannot share a name (AN1024), and a parameter cannot be void (AN1023).

  • Value types are passed by copy and objects by reference: a method that changes an object's field changes it for the caller, but assigning a parameter changes only the parameter.
  • &T and &mut T parameters take a borrowed reference, when the borrow checker is on (see Memory).
  • A parameter can carry attributes: Deposit([Range(1, 1000)] i32 amount).

Warning

Not yet available:

  • default values and named arguments (Greet(str name, str greeting = "Hello"), Greet(name: "Ana")). Write an overload that calls the full one.
  • variadic T[] parameters that take any number of arguments. Pass an array literal (Sum([1, 2, 3])), or use a pack of type parameters.

Overloads

Functions in the same scope can share a name if their parameters differ:

Antril
Describe(i32 x) : str => "the number " + x;
Describe(str s) : str => "the text " + s;
Describe(i32 x, i32 y) : str => "two numbers";

A call picks the overload:

  1. by the number of arguments;
  2. then by their types: an overload whose parameters match the arguments exactly wins over one reached through implicit conversions.

Two overloads with the same parameters are error AN1030. A call that no overload takes is AN2057 (or a conversion error when there is only one candidate); a call that two overloads take equally well is AN2058: cast an argument to say which.

Function values

A function is a value of a function type:

Type A function that
func<A, B> : R takes an A and a B and returns an R
func : R takes nothing and returns an R
func<A> takes an A and returns nothing
func takes and returns nothing

A free function, a static method, object.Method (bound to that object) or a lambda becomes a function value where one is expected. When the name is overloaded, the expected type chooses the overload. A function value is called like a function:

AntrilRun
import System;

public class Counter
{
	i32 start;

	public:
		new(i32 start) { this.start = start; }
		Step(i32 by) : i32 => start + by;
		static Triple(i32 x) : i32 => x * 3;
}

Twice(i32 x) : i32 => x * 2;
Twice(f64 x) : f64 => x * 2;

Pick(bool fast) : func<i32> : i32 => fast ? Twice : Counter.Triple;

Main()
{
	func<i32> : i32 f = Twice;
	func<f64> : f64 g = Twice;
	func<i32> : i32 bound = new Counter(10).Step;
	Console.WriteLine(f(4) + " " + g(1.5) + " " + bound(5) + " " + Pick(false)(2));

	any boxed = f;
	func<i32> : i32 back = (func<i32> : i32)boxed;
	Console.WriteLine(back(21));
}
Terminal
larva run
8 3 15 6
42

A function value can be stored in any (it is boxed) and recovered with a cast.

Warning

Not yet available:

  • naming a function type, func Transform(i32 x) : i32;. Write the func<…> : R type where it is used.
  • calling a function value with Invoke (f.Invoke(1)). Call it directly: f(1).

func*<A> : R is a different type: the address of a C function, for interop. See Interop.

Lambdas

A lambda writes a function where a value is expected:

Form Example
One parameter x => x * 2
Several (a, b) => a + b
Typed parameters (i32 a, str b) => b + a
No parameters () => 7
Block body x => { Console.WriteLine(x); }, x => { return x * 2; }

The parameter types come from the function type the lambda goes to, or are written. A lambda with untyped parameters where no function type is expected is error AN2100. The result type comes from the body; a block body must return a value on every path when one is expected (AN2001).

AntrilRun
import System;

Apply(func<i32> : i32 f, i32 x) : i32 => f(x);

Main()
{
	Console.WriteLine(Apply(x => x + 1, 41));

	func<i32, i32> : i32 add = (a, b) => a + b;
	func<i32> : func<i32> : i32 times = (i32 a) => b => a * b;
	Console.WriteLine(add(2, 3) + " " + times(3)(4));

	func greet = () => Console.WriteLine("hi");
	greet();

	i32 total = 0;
	func<i32> collect = x => { total = total + x; };
	collect(5);
	collect(7);
	Console.WriteLine(total);
}
Terminal
larva run
42
5 12
hi
12

A lambda captures the local variables and parameters it uses. It sees them as they are when it runs, and assigning one inside the lambda changes the variable itself, as total above shows. Inside an instance method, a lambda also sees this and the object's members.

Collections take lambdas: List<T> has Where, Map, Any, All, FindIndex, ForEach, RemoveAll and Sort(compare).

Extension blocks

An extension block adds functions to a type that already exists, including primitives, interfaces and types from other libraries:

Form Adds
ext(T name) { … } Methods called on values of T. Inside, name is the value the method is called on.
ext<T : constraint>(T name) { … } Methods for every type that meets the constraint.
static ext(T) { … } Static functions called on the type itself: T.F().
AntrilRun
import System;

public interface IShape
{
	Area() : f64;
}

public class Square : IShape
{
	public:
		f64 Side;
		new(f64 Side);
		Area() : f64 => Side * Side;
}

ext(i32 n)
{
	public:
		IsEven() : bool => n % 2 == 0;
}

ext<T : IShape>(T shape)
{
	public:
		Double() : f64 => shape.Area() * 2;
}

static ext(str)
{
	public:
		Repeat(str text, i32 times) : str
		{
			str all = "";
			loop (i32 i = 0..times)
				all = all + text;
			return all;
		}
}

Main()
{
	Console.WriteLine((4).IsEven());
	Console.WriteLine(new Square(3).Double());
	Console.WriteLine(str.Repeat("ab", 3));
}
Terminal
larva run
true
18
ababab
  • An extension block goes at the top level of a file or a namespace, and holds functions only: no fields, properties or constructors (AN1075).
  • A non-static block names its receiver, ext(i32 n); a static one does not, static ext(i32) (AN0029).
  • Its members take access sections like a type's. An extension is found wherever its namespace is visible: imported, or the same namespace.
  • A member of the type itself wins over an extension with the same name.

System.Core uses ext<T : bitflag enum>(T flags) to give every bitflag enum HasFlag, With, Without and Toggle.

¿Te resultó útil esta página?

No se guarda ninguna dirección; tu nombre de usuario sí, si iniciaste sesión.

Esc