io7m
io7m-jcanephora 0.44.0
2.3
API Design
2.3.1
Interfaces as capabilities
1
The package is written with one particular concept, throughout: Types represent capabilities. If the developer has a value of a given type, then the developer can perform all of the operations associated with that type.
2
This is in direct contrast to the OpenGL API, where the developer always has full access to all API functions, and individual functions (or combinations of parameters to functions) may not work based on what the implementation supports. The emphasis is on isolating the correct operations and statically preventing incorrect operations from occurring.
2.3.2
Types
1
The jcanephora package wraps all conceptually distinct objects in new types and provides sane and unsurprising functions to construct them. This prevents large classes of bugs from occurring, at compile time. It also allows the interfaces to be self-documenting. Consider the following C function prototype:
2.3.2.1. Weakly typed C function

    void _ (GLsizei, GLuint *)

2
What does the function do? It could generate a set of textures (glGenTextures), or it could generate a set of new buffer names (glGenBuffers). Without carefully reading the documentation, the required parameters to the function are certainly not obvious from the type alone. Assuming the developer has read the documentation and worked out that the correct way to call the function is with the following sequence:
2.3.2.2. Weakly typed C calls

    GLuint texture;

    glGenTextures(1, &texture);
    checkGLErrors();

3
The developer now has a value of type GLuint. Given that many functions in the OpenGL API also take values of GLuint, and those values have nothing to do with textures, that's a huge number of potential bugs that cannot be prevented by the compiler.
4
The developer now wants to allocate a new RGBA texture. The types of the glTexImage2D and glTexParameteri functions are, quite frankly, insane. Almost no developer manages to use these functions successfully without trial and error and, naturally, all errors are runtime errors. Eventually, through some painful experimentation, the developer might stumble onto the following sequence of calls:
2.3.2.3. Allocating a texture

    GLuint texture;
    void *data = makeTextureData();

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

5
The types of the parameters to all of the functions mentioned allow the developer to pass in practically anything. Only a very small subset of the possible parameters are actually valid. An even smaller subset of the possible parameters actually work across different versions of OpenGL!
6
Now consider the following jcanephora function:
2.3.2.4. Texture allocation?

    public JCGLTexture2DType _(
        JCGLTextureUnitType _,
        long _,
        long _,
        JCGLTextureFormat _,
        JCGLTextureWrapS _,
        JCGLTextureWrapT _,
        JCGLTextureFilterMinification _,
        JCGLTextureFilterMagnification _)
        throws JCGLException;

7
Even without the names of the function and the parameters, the type above make it quite clear that the function returns a JCGLTexture2DType, which is somehow derived from the arguments to the function. It's a reasonable assumption to make that the function allocates a new texture.
8
The wrapping modes on the S and T axis will presumably be derived from the parameters of type JCGLTextureWrapS and JCGLTextureWrapT respectively. The minification and magnification filters will presumably be derived from the parameters of type JCGLTextureFilterMinification and JCGLTextureFilterMagnification, respectively. The programmer is statically prevented from mixing up the two axes, or the two filter domains. The JCGLTextureFormat parameter is obviously intended to specify the format of the texture. Any parameter familiar with OpenGL texturing will be aware that any operations involving textures requires access to a texture unit, and the JCGLTextureUnitType parameter above presumably specifies the unit that will be used.
9
The two parameters of type long presumably correspond to the width and height of the resulting texture, but the types are not descriptive enough to say for sure. Unfortunately, Java lacks a way to make existing arithmetic types incompatible without sacrificing efficiency [2]. In this case, the two parameters do indeed correspond to the width and height, in that order, and the jcanephora package uses this order consistently throughout.
10
The extremely descriptive type given for the function, and the presence of declared (but unchecked) exceptions are direct contributions towards the goals of type safety and conspicuity of errors. It is very difficult for the programmer to use this function incorrectly: the type system guarantees correct usage.

[2]
New types will be new classes, resulting in all integers being replaced with pointers to integers.