Going through the demo of the new features in 0.51 I find a curious example
from numba import jit, njit, config, __version__, errors, literal_unroll, types
from numba.extending import overload
import numba
import numpy as np
assert tuple(int(x) for x in __version__.split('.')[:2]) >= (0, 51)
def demo_iv(x):
pass
@overload(demo_iv)
def ol_demo_iv(x):
# if the initial_value is not present, request literal value dispatch
if x.initial_value is None:
return lambda x: literally(x)
else: # initial_value is present on the type
print("type of x: {}. Initial value {}".format(x, x.initial_value))
return lambda x: 1
@njit
def initial_value_capturing():
l = [1, 2, 3, 4] # initial value [1, 2, 3, 4]
l.append(5) # not part of the initial value
print("out=", demo_iv(l))
initial_value_capturing() # out=1
versus (now removing literally
)
def demo_iv(x):
pass
@overload(demo_iv)
def ol_demo_iv(x):
# if the initial_value is not present, request literal value dispatch
if x.initial_value is None:
return lambda x: 2#literally(x)
else: # initial_value is present on the type
print("type of x: {}. Initial value {}".format(x, x.initial_value))
return lambda x: 1
@njit
def initial_value_capturing():
l = [1, 2, 3, 4] # initial value [1, 2, 3, 4]
l.append(5) # not part of the initial value
print("out=", demo_iv(l))
initial_value_capturing() # out=2
In the first example, the code went through the second branch (therefore producing out = 1). However, removing a function from the first branch, makes the code go through this branch (producing out = 2). That’s unexpected.
Also the changes happened at different levels. The branches are part of the typing, however the function is part of the function content, ie it was not executed during typing. So the typing is reading inside the content of the function, and changing its behaviour based on that. I know that this is what literally
is supposed to do, but it’s hard to grok.
To make it more confusing, literally
has not been imported. But it just works?
I would have found the behaviour more intuitive if literally
was placed in the caller, to clearly indicate that the argument is to be interpreted a literal
.
@njit
def initial_value_capturing():
l = [1, 2, 3, 4] # initial value [1, 2, 3, 4]
l.append(5) # not part of the initial value
print("out", demo_iv(literally(l)))
I think this is how it’s used in the documentation (https://numba.pydata.org/numba-doc/latest/developer/literal.html?highlight=literally)
I don’t know if this is how it’s supposed to work and I need to understand the internal logic, or whether this is something unintended.