We’re excited to be releasing Codon 0.19, which includes numerous updates & improvements to Python compatibility, backend code generation and all around quality-of-life. You can install and use Codon 0.19 now. Here’s a quick rundown of what’s new:
- New type checking engine: We’ve completely revamped Codon’s type checker to be able to cover some of the “hard” cases we couldn’t type check before, meaning Codon will be able to compile a wider range of Python code without any changes.
- Class fields are now inferred: Before, Codon classes had to define their fields explicitly. This is no longer the case, and fields can be inferred automatically, which means you’ll have an easier time running Python code containing classes directly in Codon without code changes.
- Function and class name resolution now matches Python: In Python, names of classes and functions are resolved when they are encountered at runtime and not at compile time when they are seen by the parser. Codon now matches this behavior.
- Better handling of functions as first-class citizens: Functions can now be passed around and stored more freely. For example, you can now have a list of
lambda
s, whereas you couldn’t before. - Better error messages: Type checking errors will now pinpoint where and why the error occurred more accurately.
- Performance improvements & backend updates: Updated to LLVM 20 for backend code generation, which also brings many performance improvements. See LLVM 20 release notes for additional context.
- Many other features: Support for
else
ontry
statements, improved support for nonlocal variables to match Python semantics, improved format strings, among many other things.
In the rest of this post, we’ll showcase some examples highlighting these new features.
Name resolution
In Python, you can reference functions or classes that have not been defined yet, given that they will be defined when the enclosing function is executed. The same now finally works in Codon – this has been one of the biggest issues when running existing Python code that historically required refactoring to work with Codon, but no longer! For example:
# source: https://github.com/exaloop/codon/issues/628
def f(n):
print('f', n)
if n > 0:
g(n - 1) # references `g`, defined below
def g(n):
print('g', n)
if n > 0:
f(n - 1)
f(10) # f 10, g 9, f 8, g 7, ..., f 0
And similarly for classes:
class A:
n: int
def __init__(self, n):
self.n = n
def foo(self):
return B(self.n + 1) # references `B`, defined below
class B:
n: int
def __init__(self, n):
self.n = n
def foo(self):
return A(self.n * 2)
a = A(1)
# B is now available, hence foo() will work
print(a.foo().foo().foo().n) # 5
Note that Codon’s name resolution now works the same as it does in Python, meaning that "forward references" outside of function scopes will not work (and similarly they don’t work in Python either).
Classes
Class fields are now inferred if no explicit fields are given. For example:
class A:
def __init__(self, z):
self.x = 'hello'
self.y = 42
self.z = z
def __str__(self):
return f'<{self.x}, {self.y}, {self.z}>'
a = A(3.14)
print(a) # <hello, 42, 3.14>
In Codon 0.18 and below, the fields x
, y
and z
would have had to be explicitly listed à la Python data classes. Now, they are inferred automatically and the code above works just like it does in Python. You’ll have a much easier time running existing Python code containing classes now!
Functions
Functions can now be passed around more freely as first-class citizens. For instance, you can now have a list of functions:
functions = [lambda x: x**i for i in range(5)]
values = [functions[i](i) for i in range(5)]
print(values) # [0, 1, 512, 19683, 262144]
Error messages
Previously, some typechecking errors would manifest as an ambiguous and hard-to-debug “cannot typecheck the program” message without additional source location information. Now, more useful error messages are given:
from collections import deque
deques = [deque() for i in range(10)]
# deques[0].append('x') # <-- can't typecheck without this line
# OLD:
# error: cannot typecheck the program
# NEW:
# test.py:2 (1-7): error: cannot typecheck 'deques'
# test.py:2 (10-38): error: cannot typecheck '[deque() for i in range(10)]'
Performance
Between LLVM 20’s own performance improvements and a few added tweaks that Codon 0.19 brings to its LLVM codegen, you’ll experience tangible performance improvements across the board. Here’s one example from the NumPy benchmark suite NPBench, specifically the Jacobi-1d benchmark:
# Adapted from NPBench's Jacobi-1d benchmark
import numpy as np
import time
def jacobi_1d(TSTEPS, A, B):
for t in range(1, TSTEPS):
B[1:-1] = 0.33333 * (A[:-2] + A[1:-1] + A[2:])
A[1:-1] = 0.33333 * (B[:-2] + B[1:-1] + B[2:])
def create_arrays(N, TSTEPS):
A = np.fromfunction(lambda i: (i + 2) / N, (N, ), dtype=np.float64)
B = np.fromfunction(lambda i: (i + 3) / N, (N, ), dtype=np.float64)
return A, B
TSTEPS = 10000
A, B = create_arrays(N=50000, TSTEPS=10000)
t0 = time.time()
jacobi_1d(TSTEPS, A, B)
t1 = time.time()
print(A.sum(), B.sum())
print('time:', t1 - t0, 'seconds')
Here are the timings on an M1 MacBook Pro with the -disable-exceptions
flag:
- Codon 0.18.2 – 1.20 seconds
- Codon 0.19.0 – 0.34 seconds (>3.5x speedup)
By the way, Codon 0.19 is 2x faster than Python 3.13 with NumPy 2.2 here as well. You can learn more about Codon-NumPy, and how Codon improves NumPy performance, in our previous blog post.
A few other improvements
You can now use else blocks on try statements as in Python:
from sys import argv
try:
num = int(argv[1])
except ValueError:
print(f'{argv[1]} is not an integer')
else: # <-- now works in Codon!
print(num)
and
and or
now give values of type corresponding to their operand types like in Python:
m = 0
n = m or 42
print(n) # now '42' as in Python; previously 'True'
Better handling of captured / nonlocal variables to match Python semantics:
def foo():
a = 'hello'
f = lambda: a + '!'
a = 'world'
return f
f = foo()
print(f()) # now 'world!' as in Python; previously 'hello!'
… among many other improvements! See the release notes for more details.
What’s next: Codon 1.0
The next major milestone for Codon is the 1.0 release, for which we have several exciting projects that we hope to complete over the coming weeks and months. Among those are:
- Full unicode string support
async
/await
support- Revamp and expansion of Codon’s GPU programming support
- Support for alternative memory management strategies in addition to the conservative garbage collection that Codon uses today
As always, we welcome feedback and suggestions as we continue to develop Codon. Is there a particular module or library that you’d like included natively? A particular issue or roadblock you’ve run into? A specific use case you're interested in? Let us know on GitHub and on Discord!
Thanks for reading, and happy coding!