Anexo · Mojo: Python con velocidad de sistemas¶
Anexo informativo. Mojo no forma parte de las dependencias del curso ni se instala con el proyecto; esta página es introductoria, para orientarte y comparar lenguajes. Los ejemplos proceden de la documentación oficial y necesitan el SDK de Mojo para ejecutarse.
Qué es Mojo¶
Mojo es un lenguaje de programación creado por Modular (fundada por Chris Lattner, creador de LLVM y Swift, junto a Tim Davis). Su propuesta es ambiciosa: combinar la facilidad de Python con el rendimiento de C/C++/Rust, sobre todo para inteligencia artificial y cálculo intensivo.
La idea central es que Mojo ofrece una sintaxis familiar e interoperabilidad
con Python, pero no garantiza compatibilidad total: parte del código Python
se ejecuta tal cual y otra parte necesita adaptaciones. Sobre esa base puedes
añadir características de sistemas —tipos estáticos, estructuras struct y
gestión de memoria segura— para hacer el programa más especializado.
Lo que hace especial a Mojo:
- Rendimiento de nivel C/Rust en cómputo numérico y bucles tensos (compilado y optimizado en tiempo de compilación).
- Sin GIL: programación concurrente de verdad.
- Puerto a GPU y aceleradores de forma nativa.
- Interop con Python: puede importar muchas librerías de Python usando el runtime de CPython, aunque la interoperabilidad no equivale a compatibilidad al 100 %.
- Memoria segura con borrow checker (
owned/borrowed).
Estado: ahora es open source¶
Hasta hace poco el compilador de Mojo era cerrado (solo la biblioteca estándar
y parte del código de kernels eran abiertos). El 18 de agosto de 2026, tras
alcanzar Mojo la versión 1.0 con estabilidad de código fuente, Modular abrió
el compilador y las herramientas bajo licencia Apache 2.0 (con excepciones
LLVM), en el repositorio de GitHub modular/modular (anuncio
oficial).
Instalación con uv¶
Mojo se instala "como un paquete de Python o de Conda" en macOS y Linux. La
documentación oficial admite uv y pixi como gestores equivalentes; aquí
usamos uv, el del curso. Primero, instala uv si no lo tienes:
Instalación en un proyecto:
Windows
En Windows, Mojo se usa vía WSL2 (subsistema de Linux), no de forma nativa. Si
quieres el build nightly (más nuevo, con posibles bugs), añade el índice:
uv add mojo --index https://whl.modular.com/nightly/simple/ --prerelease
allow. Modular ha anunciado cambios de ubicación de ese índice; si falla,
consulta la guía de instalación oficial.
Tu primer programa¶
Crea hola.mojo:
Ejecuta:
Salida esperada:
Todo programa Mojo usa main() como punto de entrada. También puedes pedir
datos al usuario (input() es built-in) y compilar a un ejecutable nativo:
Salida esperada (escribiendo Edna):
Como input() puede fallar, main() lleva raises. Y si quieres un binario
que no dependa del intérprete:
Compilado, no interpretado
Al ejecutar con mojo hola.mojo, Mojo compila just-in-time y luego
corre el resultado; con mojo build obtienes un ejecutable independiente.
Un ejemplo guiado (del quickstart oficial)¶
Mojo se parece mucho a Python, pero introduce tipos, var y algunas
diferencias de sintaxis. Un analizador de temperaturas:
def calcular_promedio(temps: List[Float64]) -> Float64:
var total = 0.0
for temp in temps:
total += temp
return total / Float64(len(temps))
def main():
var temps: List[Float64] = [20.5, 22.3, 19.8, 25.1]
print("Registradas", len(temps), "temperaturas")
var media = calcular_promedio(temps)
print("Media:", round(media, 2))
Salida esperada:
Diferencias frente a Python que conviene ver aquí:
- Usamos
varpara dejar explícita la intención de trabajar con una variable mutable; la documentación estable actual no debe presentarse como siletgarantizara inmutabilidad. Mojo infiere el tipo o lo anotas (List[Float64]). - Las firmas de función indican tipos de parámetros y de retorno (
-> Float64). t"..."crea cadenas plantilla que interpolan sin asignaciones intermedias.Float64(len(temps))convierte el entero aFloat64para la división.
Declarar variables¶
En este anexo usamos var para las variables que pueden cambiar. El tipo se
puede anotar o dejar que Mojo lo infiera:
def main():
var nombre: String = "Mojo"
var contador = 0
print("Nombre:", nombre) # valor inicial
nombre = "Mojo 1.0" # reasignar: `var` permite cambiar el valor
contador += 1
print("Nombre:", nombre) # valor reasignado
print("Contador:", contador)
Salida esperada:
Colecciones: List tipada¶
Las colecciones de Mojo son homogéneas y tipadas en tiempo de compilación:
List[Int] solo puede contener enteros, y el compilador lo comprueba. El
[Int] entre corchetes es un parámetro de tipo (similar a list[int] en
Python moderno, pero comprobado al compilar):
def main():
var nums: List[Int] = [12, -7, 64]
nums.append(-937)
print("Numero de elementos:", len(nums))
print("Ultimo elemento (pop):", nums.pop())
print("Primero:", nums[0], "| Segundo:", nums[1])
print("Ultimo:", nums[len(nums) - 1])
Salida esperada:
Bucles con for y plantillas¶
El bucle for...in funciona como en Python, y las plantillas t"..." interpolan
valores directamente en el texto:
def main():
var dias: List[String] = ["Lunes", "Martes", "Miércoles"]
for dia in dias:
print(t"Día: {dia}")
Salida esperada:
Condicionales¶
if/elif/else también son casi idénticos a Python; la diferencia es que los
valores numéricos y los tipos son estáticos:
def clasificar(media: Float64) -> String:
if media > 25.0:
return "Semana calurosa"
elif media > 20.0:
return "Semana templada"
else:
return "Semana fresca"
def main():
var media = 21.92
print(clasificar(media))
Salida esperada:
Un caso TechShop: totales y mejor pedido¶
Lo anterior, junto en un mini-caso del caso conductor: recorrer una lista de importes, acumular el total y localizar el máximo:
def main():
var ventas: List[Float64] = [120.5, 98.3, 143.0, 87.25]
var total = 0.0
for venta in ventas:
total += venta
var media = total / Float64(len(ventas))
print("Ventas:", len(ventas), "pedidos")
print(t"Total: {round(total, 2)} € | Media: {round(media, 2)} €")
var mejor = ventas[0]
var indice = 0
for i in range(len(ventas)):
if ventas[i] > mejor:
mejor = ventas[i]
indice = i
print(t"Mejor pedido: {mejor} € (dia {indice + 1})")
Salida esperada:
Fíjate en Float64(len(ventas)): len devuelve Int, y Mojo no mezcla
Int con Float64 en una división sin que conviertas explícitamente. Es la
misma idea que int()/float() en Python, pero comprobada al compilar.
Manejo de errores¶
Una función puede indicar que puede lanzar una excepción (raises) y se
maneja con try/except:
def calcular_promedio(temps: List[Float64]) raises -> Float64:
if len(temps) == 0:
raise Error("No hay datos de temperatura")
var total = 0.0
for temp in temps:
total += temp
return total / Float64(len(temps))
def main():
try:
var media = calcular_promedio([]) # lista vacía
print("Media:", media)
except e:
print("Error:", e)
Salida esperada:
Usar librerías de Python¶
Mojo importa módulos de Python tal cual:
from std.python import Python
def main() raises:
var np = Python.import_module("numpy")
print(np.mean([20.0, 22.0, 19.0, 25.0]))
Salida esperada:
Dos detalles de Mojo 1.0: la llamada puede fallar en tiempo de ejecución
(NumPy podría no estar instalada), así que main() se marca raises; y las
listas pasan a NumPy como Float64. Requiere tener numpy instalada en el
entorno (uv add numpy).
Structs: crear tu propio tipo¶
El equivalente Mojo de una clase Python es el struct, y tiene reglas
propias: los campos se declaran con var y su tipo, todo se inicializa en el
constructor, y la estructura queda fijada al compilar (no puedes añadir
campos al vuelo como en Python). El decorador @fieldwise_init genera el
constructor campo a campo:
@fieldwise_init
struct Producto:
var nombre: String
var precio: Float64
def con_iva(self) -> Float64:
return self.precio * 1.21
def main():
var raton = Producto(nombre="Raton inalambrico", precio=24.9)
var monitor = Producto(nombre="Monitor 24", precio=189.0)
print(raton.nombre, "-", raton.precio, "€")
print(t"Con IVA: {round(raton.con_iva(), 2)} €")
print(t"Monitor con IVA: {round(monitor.con_iva(), 2)} €")
Salida esperada:
Para guardar structs en una List (o recorrerla), el tipo debe ser copiable:
se declara con los rasgos Copyable y Movable (Mojo genera los constructores
de copia y movimiento por ti). Es el equivalente a la distinción copia/referencia
que viste en 1.6 · Listas y tuplas,
aquí explícita y comprobada al compilar:
@fieldwise_init
struct Producto(Copyable, Movable):
var nombre: String
var precio: Float64
def con_iva(self) -> Float64:
return self.precio * 1.21
def es_caro(self) -> Bool:
return self.precio > 100.0
def main():
var catalogo: List[Producto] = [
Producto(nombre="Raton", precio=24.9),
Producto(nombre="Teclado", precio=79.9),
Producto(nombre="Monitor", precio=189.0),
]
var total = 0.0
for p in catalogo:
print(p.nombre, "-", p.precio, "€ | caro:", p.es_caro())
total += p.precio
print("Total catalogo:", round(total, 2), "€")
Salida esperada:
Raton - 24.9 € | caro: False
Teclado - 79.9 € | caro: False
Monitor - 189.0 € | caro: True
Total catalogo: 293.8 €
Fíjate en dos diferencias con una clase Python: los métodos que modifican
el struct deben declarar mut self (por defecto self es inmutable), y no hay
herencia — la reutilización se consigue con traits, que quedan fuera de este
anexo.
Comparación con Python "normal"¶
| Aspecto | Python | Mojo |
|---|---|---|
| Ejecución | Interpretado | Compilado |
| Tipos | Dinámicos | Estáticos (con inferencia) + struct |
| Concurrencia | Con GIL (limitada) | Sin GIL (paralelismo real) |
| Rendimiento (cómputo numérico) | Depende de las librerías | Potencialmente alto en código compilado; hay que medir |
| Biblioteca estándar | Enorme y madura | Creciente (puede usar la de Python) |
| Gestión de memoria | GC / refcount | Owned/borrow (segura) |
| IA y GPU | Vía librerías | Nativo (aceleradores, kernels) |
| Curva de aprendizaje | Baja | Media-alta (conceptos de sistemas) |
| Madurez | Muy maduro | 1.0, en evolución rápida |
A matizar
Mojo no siempre es más rápido que Python: depende de la carga. Destaca en cómputo numérico y bucles tensos; en ciertas operaciones de texto o I/O da resultados comparables (e incluso ha habido casos más lentos). Además es joven: no todo Python funciona tal cual y el ecosistema nativo aún crece. Considera Mojo como una herramienta a vigilar, no como sustitución de Python hoy.
¿Te interesa en este curso?¶
Para Programación de IA, Mojo es una lectura y experimentación interesante: refuerza la idea de que se puede partir de Python y bajar a sistemas (tipos, compilación, aceleradores) cuando el rendimiento importa. No sustituye a Python ni al material del curso: es un complemento para ampliar el horizonte y ver por qué el lenguaje elegido en el curso es Python.