Improve @guvectorize capabilities

Currently, @guvectorize functions works by compiling the functions ahead of time. The decorator expects the list of signatures as well as the declaration of input and output layouts:

@guvectorize([(int64[:], int64, int64[:])], '(n),()->(n)')
def g(x, y, res):
    for i in range(x.shape[0]):
        res[i] = x[i] + y

g in this case will only work with int64 types and calling it with different types will not work. I propose we make the list of types optional in @guvectorize:

@guvectorize([(int64[:], int64, int64[:])], '(n),()->(n)')
def g(x, y, res):
    for i in range(x.shape[0]):
        res[i] = x[i] + y

@guvectorize('(n),()->(n)')
def f(x, y, res):
    for i in range(x.shape[0]):
        res[i] = x[i] + y


x = np.arange(5, dtype=np.int32)
y = nb_types.int32(10)
res = np.zeros(5, dtype=np.int32)
g(x, y, res) # Compile a new version for `types.int32`
f(x, y, res) # infer signature based on argument types

Note that the declaration of inputs and output layout is still present.

My question is, is there any downside of this approach? Should I be aware of any compatibility break if I move forward to implement this?

I think this is a good idea as it will fix an API hole that @vectorize can do this but not the @guvectorize. I am not aware of any downside esp if the AOT behavior is still available.

For reference, take a look at https://github.com/numba/numba/blob/master/numba/np/ufunc/dufunc.py for how @vectorize does this.

Currently, in Numba (and in Numpy), it is possible to call a guvectorized function as res = g(x, y) rather than g(x, y, res). Having this pattern when types are optional is a bit problematic, since we would have to guess the output type and shape based on signature ((n),()->(n)) and input types (x, y). Should this pattern be forbidden in Numba when types were not specified? cc @sklam

Should I move this discussion to github issues?