CEP 509 - Method templates
Implementation status: Not started
This is about "template methods": Methods that have a set of compile-time parameters (types and constants) and are duplicated once for each used combination of parameters.
The way this will used is to at compile-time create different "views" of an object, each view having different versions of methods. The underlying object is not "templates" or "parametrized" at all though. This is useful for integrating with libraries.
For creating new, template-based libraries within Cython, type templates (not described here) are a better solution. They are much less complicated to grasp, as they create a new run-time type for each combination of type argument. However, sometime one does not want to create a real new type, only simulate it in order to generate different code at compile-time.
The syntax below depends on function overloading, however alternative syntax could more explicitly use only the type arguments so that this requirement isn't in any way absolute.
- Only cdef methods are supported for now.
- Some new syntax (in the examples below I use a decorator @cython.template, but there are arguments against this too) is introduced to mark a method as a template. Such methods must be fully implemented in the pxd file. Also it should be specified which compile-time arguments it takes.
A method declared as a template method is not callable if the compile-time arguments cannot be resolved (as will happen if the necesarry type arguments have not been provided).
- When a call to the method with a new combination of known compile-time arguments is compiled, the method implementation is copied, the name mangled and the compile-time arguments substituted. An entry is made in a map so that subsequent calls the compiler sees will call the same instance of the method.
More syntax notes
The argument against @cython.template is that the method defined beneath it uses symbols that has significance as template parameters to the parser, and decorators thus might seem unnatural. Therefore a new keyword (template?) might be in order instead.
Guido once proposed using brackets for template arguments in a blog...something like this:
def foo(T a) [T]: ...
Python-like duck typing syntax
An alternative would also be to have
cdef foo(a, b, c): ...
automatically (or with a decorator or similar) do the same as (using Guido's template syntax):
cdef foo(A a, B b, C c) [A, B, C]: ...
which would basically allow for "compile-time duck-typing". While one might want to leave this for when template support is in place and stable, it could definitely be picked up again if Cython starts development for type inference etc.
typeof(a) could be used to reference the implicit A within the function.
One way of doing this could be to change the default rule of no type means object to no type means templated argument type. One could still specify object as the type if a template isn't wanted. This would make for a compelling syntax, but will break backwards compatability.
Because this is only template methods and not a template class things might look a bit unusual, but for some situations they are still just right.
NumPy is one such example. The following schetches two operators and how to use them:
cdef typedef struct numpy.ndarray ....: typeargs: type dtype int nd ... @cython.template("dtype", "nd") cdef dtype __getitem__(numpy.ndarray(dtype, nd) self, object indices): # code for accessing item # note that return type is specified # hypothetical Cython-specific operator to get the idea across @cython.template("dtype", "nd") def __coercefromobject__(numpy.ndarray(dtype, nd) self, x): if not isinstance(x, numpy.ndarray): raise TypeError(...) if x.dtype != dtype: raise ValueError(...) if x.dtype return x
a = numpy.zeros([10, 10], dtype=numpy.uint64) # print a[4, 4] would give compile-time error, no compile-time args for __getitem__ c_numpy.ndarray(c_numpy.uint64, 2) b = a print b[4, 5] c_numpy.ndarray(c_numpy.uint64, 4) c = a
This would instantiate one version of __getitem__ and two versions of __coercefromobject__.
One could also support functions:
@cython.template("T") cdef T doubler(T value): return value * 2 cdef float f = doubler(4.5) cdef int x = doubler(8)
Here, two versions of the doubler functions will be present in the C file, one for int and one for float.