Scala no es solo la JVM. Dos proyectos permiten llevar Scala a otros entornos de ejecución: Scala.js compila a JavaScript para correr en el navegador o en Node.js, y Scala Native compila a código nativo vía LLVM para ejecutables sin JVM. Ambos han madurado bastante y en 2026 son opciones reales para casos de uso específicos.
Scala.js: Scala en el navegador
Scala.js lleva activo desde 2013 y es el más maduro de los dos. Compila código Scala a JavaScript optimizado, lo que permite compartir código entre el servidor (JVM) y el cliente (navegador). Esto es especialmente útil para validaciones, modelos de dominio o lógica de negocio que necesitas en ambos lados.
import scala.scalajs.js
import scala.scalajs.js.annotation.*
import org.scalajs.dom.*
@JSExportTopLevel("mostrarMensaje")
def mostrarMensaje(texto: String): Unit =
val div = document.createElement("div")
div.textContent = texto
document.body.appendChild(div)
El plugin sbt-scalajs integra la compilación en el proceso de build de SBT. El código resultante puede usar el ecosistema npm, ya sea a través de facades tipadas (que definen los tipos de la API JavaScript para que el compilador Scala las conozca) o mediante js.Dynamic para acceso sin tipos:
import scala.scalajs.js
import scala.scalajs.js.Dynamic.global
// Acceso dinámico (sin tipos, como en JS puro)
global.console.log("Mensaje desde Scala.js")
val fecha = js.Date()
// Facade tipada para una librería JS
@js.native
@JSImport("some-library", JSImport.Default)
object MiLibreria extends js.Object:
def metodo(param: String): String = js.native
El output de Scala.js puede ser bastante grande si no se optimiza, pero con el modo de producción (fullOptJS) aplica tree-shaking agresivo y el resultado es comparable a otros compiladores de lenguajes tipados a JS.
Compartir código entre JVM y JS
La parte más potente de Scala.js es la capacidad de tener módulos multiplataforma que compilan tanto para JVM como para JS. En SBT, esto se hace con los «cross projects»:
// build.sbt con módulo compartido
lazy val shared = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.in(file("shared"))
.settings(
libraryDependencies += "io.circe" %%% "circe-core" % "0.14.9"
)
lazy val sharedJVM = shared.jvm
lazy val sharedJS = shared.js
El %%% en lugar de %% indica que la dependencia debe resolverse para la plataforma correcta (JVM o JS). Muchas librerías del ecosistema Scala publican artefactos para ambas plataformas.
Scala Native: sin JVM
Scala Native usa LLVM como backend para compilar a código nativo. El resultado es un ejecutable que arranca en milisegundos (sin la carga de la JVM) y puede llamar a código C directamente. Es ideal para herramientas de línea de comandos, scripts de sistema y casos donde el tiempo de arranque importa.
// Interop con C en Scala Native
import scala.scalanative.unsafe.*
import scala.scalanative.libc.stdio.*
@main def leerFichero(ruta: String): Unit =
Zone:
val cRuta = toCString(ruta)
val f = fopen(cRuta, c"r")
if f != null then
val buf = stackalloc[Byte](1024)
while fgets(buf, 1024, f) != null do
print(fromCString(buf))
fclose(f)
else
println(s"No se pudo abrir $ruta")
Scala Native 0.5: las mejoras clave
La versión 0.5 de Scala Native (2024) trajo mejoras importantes. El recolector de basura Commix (basado en el diseño de Boehm GC pero con mejoras de concurrencia) reduce significativamente las pausas en aplicaciones con uso intensivo de memoria. El soporte de multithreading mejoró: ahora Scala Native puede usar múltiples hilos del sistema operativo de forma más estable que en versiones anteriores.
El interop con C también mejoró, con mejor soporte para punteros y estructuras C complejas, lo que facilita el uso de librerías del sistema como libcurl, OpenSSL o cualquier biblioteca con API C.
Casos de uso reales
Scala Native encaja bien en:
- Herramientas CLI que necesitan arranque rápido (donde la JVM tardaría 1-2 segundos en iniciar).
- Scripts de procesamiento de ficheros que se lanzan frecuentemente.
- Código que necesita interoperabilidad con sistemas en C o con el sistema operativo a bajo nivel.
- Contextos donde instalar una JVM no es una opción.
Scala.js encaja en:
- Aplicaciones web donde quieres compartir validaciones o modelo de dominio entre servidor y cliente.
- Proyectos donde el equipo prefiere Scala sobre TypeScript pero necesita código en el navegador.
- Librerías que quieren publicar para ambas plataformas.
Limitaciones actuales
Ni Scala.js ni Scala Native tienen acceso al ecosistema JVM completo. Librerías que dependen de reflexión Java, de clases internas de la JVM o de APIs de java.nio avanzadas no van a funcionar sin adaptar. El ecosistema Typelevel (Cats, ZIO) tiene soporte parcial para ambas plataformas pero no todo funciona. Antes de adoptar cualquiera de los dos, conviene verificar si tus dependencias tienen artefactos para la plataforma objetivo.
Para la JVM tradicional y proyectos de datos, el artículo de Scala con Apache Spark cubre esa parte. Y para quien venga de JavaScript y quiera explorar tipado estático, merece la pena ver también qué ofrece Kotlin con su soporte multiplataforma.
Imagen: Pexels / Markus Spiske
