-
Notifications
You must be signed in to change notification settings - Fork 11
3. Functions
Gator's functions are not first class (see #89). Gator supports function overloading, parameterization, and both builtin and operation declarations.
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
}
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);
}
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.