Skip to content

3. Functions

Dietrich Geisler edited this page Apr 15, 2020 · 2 revisions

Gator's functions are not first class (see #89). Gator supports function overloading, parameterization, and both builtin and operation declarations.

Overloading

Functions must be declared with explicit argument and return types. All invocations of that function must include all arguments (i.e. no partial applications) of the target overloaded function. Additionally, overloaded functions must be unambiguous when invoked; if more than one function can be applied, an error will be thrown. For example, the following behavior holds:

int[2] add(int[2] x, int[2] y) {
  return [x[0] + y[0], x[1] + y[1]];
}

int[3] add(int[3] x, int[3] y) {
  return [x[0] + y[0], x[1] + y[1], x[2] + y[2]];
}

// This is bad behavior, but no error is thrown until invocation
int[2] add(int[3] x, int[3] y) {
  return [x[0] + y[0], x[1] + y[1]];
}

// Main is the one exception: it cannot be overloaded, must be void, and have 0 arguments
void main() {
  print add([1, 2], [2, 3]); // [3, 5]
  print add([1, 2, 3], [2, 3, 4]); // Error: ambiguous function overloading
}

Parameterization

As with types, functions can be parameterized freely on types and geometry type components (such as frames). Functions use the same with syntax as types for this feature; as with types, function parameterizations can often be inferred based on the argument types. As an example of function parameterization, we will examine using type parameterization to store and unwrap type information about texture arguments:

// Wrap a 4D vector in sampler2D
with float[4] T:
declare type sampler2D;

// Require a sampler2D with wrapped float[4] type 'T'
with float[4] T:
declare T texture2D(sampler2D<T> texture, float[2] coord);

type color is float[4];

// Has internal type 'color'
uniform sampler2D<color> uTexture;
varying float[2] vTexCoord;

void main() {
  // Using texture2D, we 'unwrap' the type color
  // Note that we can omit '<color>' as it can be inferred from uTexture
  color gl_FragColor = texture2D<color>(uTexture, vTexCoord);
}

Special Declarations

Gator functions can be declared as builtin, for inset operations, and as canonical. Declaring a builtin function is as simple as using the declare keyword with a function header and without a function body. For example, we can declare the dot product to work with the GLSL vec types as follows:

type vec2 is float[2];
type vec3 is float[3];
type vec4 is float[4];

// Note that dot is overloaded (correctly)
declare float dot(vec2 x, vec2 y);
declare float dot(vec3 x, vec3 y);
declare float dot(vec4 x, vec4 y);

We can declare the type and behavior of unary and binary inset operations in Gator (indeed, these operations have no association by default). To do so, we declare a function with the name of the operation and appropriate number of arguments:

// Note that - is both a unary and binary operation
declare int -(int x, int y);
declare int -(int x);

int[2] -(int[2] v) {
  // Here we use the builtin definition for negation
  return [-v[0], -v[1]];
}

Finally, functions can be declared canonical, meaning they can be used with in expressions. For more details, see the follow-up tutorial on casting.