Functions

Functions, calls, and staging

TinyFX functions are statically typed and may be overloaded. The checker binds each call to one declaration, maps authored arguments to parameters, applies defaults, records coercions, and passes that decision to every backend. This page explains source rules; the generated operation reference is the authoritative index of builtin and host operation signatures.

See types for function, trait, and resource types, and GPU programming for shader and compute-specific rules.

Declaring functions

An ordinary function uses fn. Parameters have types; a return type is optional and is inferred from return statements when omitted.

fn dot2(a: float2, b: float2) -> float {
  return a.x * b.x + a.y * b.y;
}
 
fn brighten(color: float4, factor: float = 1.1) {
  return (color.rgb * factor, color.a);
}

Several declarations may share a name when their parameter types make calls unambiguous. The checker selects the best matching overload; an equally good match is an error. Return type alone does not distinguish overloads.

Functions may be declared pub, but imports presently do not provide file-scoped module visibility. pub is meaningful for struct/enum fields and methods, which are otherwise private; see types.

Calls, defaults, and named arguments

Calls can use positional arguments, then named arguments in any parameter order. Positional arguments must come before the first named argument; no parameter may be supplied twice. Omitted parameters must have defaults.

fn orbit(
  target: float3,
  distance: float = 500.0,
  yaw: float = 0.0,
  pitch: float = 0.4,
) -> float3 {
  // ...
  return target;
}
 
let a = orbit((0.0, 0.0, 0.0));
let b = orbit((0.0, 0.0, 0.0), yaw: 1.2, distance: 320.0);

Argument expressions still evaluate in authored source order, even when names reorder their receiving parameters. Defaults are evaluated by the callee in its own declaration context, rather than reconstructed by each caller.

Named arguments are a general function/method feature, not a guarantee that every special form accepts labels. Function values and matrix constructors are positional; drawAdvanced is named-only; and an operation can document its own aliases or restrictions. Consult the operation reference when calling a builtin or host operation.

Methods and static APIs

An impl method with self as its first parameter is an instance method; one without self is a static method. self must be first.

struct Range { pub low: float, pub high: float }
 
impl Range {
  pub fn contains(self, value: float) -> bool {
    return value >= self.low && value <= self.high;
  }
 
  pub fn unit() -> Range {
    return Range { low: 0.0, high: 1.0 };
  }
}
 
let unit = Range::unit();
let ok = unit.contains(0.25);

Fields and methods are private unless pub; code outside the owning impl cannot bypass that rule. The same syntax powers namespace-style standard library APIs: sdf::sphere, noise::fbm2, and matrix::lookAt are public static methods on deliberately empty structs.

Arrow functions and function values

Arrow expressions are the function values in TinyFX. Their parameters are typed and their body is either one expression or a block.

let square: fn(float) -> float = (value: float) => value * value;
let shifted = (value: float) => {
  return value + 1.0;
};
 
print(square(3.0));

Named free functions and static methods are not first-class values: call them with (...) rather than passing their name. A function-value call is positional and requires exactly its declared parameters.

On CPU paths, arrows close over their surrounding values. In GPU code they have a narrower model: a literal arrow may be passed directly to a GPU-compatible higher-order call, where the compiler lifts and specializes it for that call site. Storing an arrow in a GPU local, passing an arrow variable, or forwarding a function value through another GPU call is not supported. Keep the literal at the GPU call site, and read captures and higher-order GPU code before designing a shader helper around it.

Execution stages

Functions have three annotations:

  • gpu fn (or gpu) promises that the function is GPU-compatible. It is validated as such and may also be called from CPU code.
  • cpu fn (or cpu) is CPU-only and can never be reached from a shader or compute kernel.
  • An ordinary fn has no declared stage. It may be used from CPU code, and it is checked recursively for GPU compatibility when a GPU site reaches it.
gpu fn luminance(color: float3) -> float {
  return dot(color, (0.2126, 0.7152, 0.0722));
}
 
cpu fn titleForFrame(frame: int) -> string {
  return "frame=" + frame.toString();
}

GPU compatibility includes the full reachable call chain, parameter and return types, resource identities, and stage-specific operations. A cpu function, dynamic collection, string operation, trait call, or incompatible capture makes a GPU-reachable path invalid. GPU recursion is also rejected because WGSL cannot represent it. Diagnostics include the call chain that introduced the restriction.

Resource parameters are supported for reusable GPU helpers, but must resolve to a statically known capture, global-rooted path, or forwarded resource parameter at the GPU call site. The compiler specializes the WGSL helper for that resource vector; dynamic resource selection and returning a resource from a GPU-compatible function are rejected. See buffers and texture ownership.

Targets and output

A GPU-compatible function can be emitted for VM execution and WGSL when its uses need both. The generated TypeScript/WebGPU target is a separately lowered but broadly supported execution path, not a fullscreen-only prototype or VM bytecode translated to JavaScript. Precise generated-target coverage remains operation-specific; use the operation reference and compiler outputs when selecting a deployment path.

  • Language — program structure, metadata, imports, and control flow.
  • Types — aggregates, enums, traits, and CPU/GPU data boundaries.
  • GPU programming — shader calls, captures, resources, and compute.
  • Runtime — lifecycle calls and host behavior.
  • Operation reference — generated signature and target reference.