El camino a Dart 3: Un lenguaje completamente sound, null safe

Preparándonos para el próximo lanzamiento mayor, donde Dart solo soporta sound null safety

En los últimos cuatro años, hemos evolucionado Dart hasta convertirlo en un lenguaje rápido, portátil y moderno. Nuestro próximo lanzamiento, Dart 3, completa el camino hacia un lenguaje completamente con sound null safety. Como último paso de ese camino, estamos eliminando varios artefactos históricos del lenguaje Dart y del SDK, incluyendo la eliminación del soporte para ejecutar sin sound null safety. Esto hace que Dart sea más fácil de aprender y nos permite evolucionar el Dart SDK para admitir nuevas características con mayor velocidad. Para conocer los principales cambios en Dart 3 y cómo preparar mejor tus apps y paquetes, ¡sigue leyendo!

Un camino sinuoso que representa el camino hacia Dart 3.
El camino hacia Dart 3. ( Foto por Niklas Ohlrogge en Unsplash )

¿Por qué sound null safety?

#

Muchos lenguajes de programación modernos admiten null safety (también conocido como void safety). Esto evita problemas de desreferenciación de null, que Tony Hoare calificó como un error de mil millones de dólares: "This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years" (Hoare 2009). Para resolver esto, lenguajes como Swift, C# y Kotlin admiten un sistema de tipos donde puedes declarar variables como non-null (pueden nunca contener un valor null) o nullable (pueden contener un valor o null). Este sistema de tipos se puede combinar con análisis estático para detectar cualquier asignación de null a variables non-null. El soporte de null safety en el lenguaje Dart usa un modelo similar, haciendo que las variables sean non-null por defecto, y solo permitiendo nulls cuando se declaran explícitamente.

Llevándolo un paso más allá, Dart introduce sound null safety en un lenguaje existente. Un sistema sound garantiza que una variable non-null nunca contiene un valor null. Durante la planificación temprana de null safety en Dart, exploramos este tema y los compromisos que otros lenguajes habían tomado. El diseño de Swift incluía soundness desde el principio. TypeScript es inherentemente unsound, ya que su sistema de tipos subyacente permite ver cualquier objeto como cualquier tipo estático. Como puedes ver en este ejemplo de TypeScript, podemos asignar un valor null a una variable non-null. Por razones ergonómicas y para evitar la migración de todo el código existente, C# tiene varias excepciones a sus comprobaciones de null. Kotlin tiene varias excepciones unsound excepciones, en parte debido a su objetivo de interoperar con Java. Como puedes ver en este ejemplo de Kotlin, generic types pueden desencadenar casos donde los valores null pueden fluir hacia una lista declarada como que contiene elementos non-null.

Para Dart, elegimos el camino de sound null safety. Esto implicó un compromiso. En algunos casos, esto hizo que la migración a null safety fuera un poco más costosa pero resulta en un sistema de tipos sound con confianza completa en las anotaciones de tipo non-null. Podemos hacer implementación optimizations en nuestros compiladores y runtimes basadas en el aspecto de nullabilidad del sistema de tipos. Sabemos exactamente cuando una variable no es null. Creemos que estos fueron el conjunto correcto de compromisos para Dart.

En Dart 3, todo el código de Dart usará sound null safety

#

Tracking issue: SDK #49530.

Han pasado tres años desde que introdujimos null safety en el lenguaje Dart en Dart 2.12. Como se mencionó en la sección anterior, reconocemos el impacto necesario para migrar los paquetes y apps de Dart existentes. Para ayudar con la migración, Dart ha admitido ejecutar el código de tu app de tres maneras. Puede ejecutarse sin null safety, en un modo mixto con null safety parcial, o con sound null safety completo. El sound null safety completo ocurre cuando el 100% del código, incluyendo todas las dependencias, ha sido migrado. Esto dio tiempo a los desarrolladores de Dart para migrar el código existente paso a paso. Sin embargo, tener soporte para varios modos añadió sobrecarga y complejidad.

Primero, los desarrolladores de Dart necesitan estar conscientes de los tres modos. Siempre que leas un fragmento de código Dart, debes verificar la versión del lenguaje para ver si los tipos son non-null por defecto, nullable por defecto, o alguna combinación de los mismos.

Segundo, admitir los tres modos en nuestros compiladores y runtimes ralentiza la evolución del Dart SDK. Este soporte aumenta el costo y la complejidad de agregar nuevas características.

En Dart 3, sound null safety será, como se mencionó y anunció anteriormente, el único modo admitido. Los archivos pubspec con una restricción del SDK que tenga un límite inferior menor a 2.12 dejarán de resolverse en Dart 3 y versiones posteriores. Cualquier código fuente que contenga marcadores de lenguaje fallará cuando configures la restricción a menos de 2.12 (ej. // @dart=2.9).

Según nuestra telemetría, creemos que alrededor del 85% de todas las ejecuciones de flutter run usan sound null safety en este momento. Si tienes apps o paquetes en el 15% restante, por favor migra antes de que se lance Dart 3, que esperamos alrededor de mediados de 2023.

Entendemos que las migraciones de bases de código grandes pueden tomar algo de tiempo. Creemos que encontrarás que la migración vale el esfuerzo. BMW recientemente migró su app principal MyBMW, una app muy grande creada por un equipo de alrededor de 300 desarrolladores:

"Aunque la migración a null-safety ciertamente no fue fácil para una base de código a gran escala como la app MyBMW, las herramientas de Google nos dieron gran asistencia en el proceso de migración. Después de haber completado la migración disfrutamos tener una base de código menos propensa a errores.", Christian Schmid, BMW AG

Para aprender más sobre la migración, mira el siguiente video, o revisa la guía de migración.

Ver en YouTube en una nueva pestaña: "Cómo migrar paquetes de Dart a null safety"

Cambios rupturantes en el lenguaje y la API

#

Junto con el cambio de null safety, estamos haciendo algunos otros cambios para eliminar artefactos históricos en el lenguaje Dart y las APIs de la biblioteca principal. Estos cambios incluyen eliminar APIs de la biblioteca principal discontinuadas (#49529), eliminar una sintaxis histórica para valores de parámetros predeterminados (#2357), y requerir que los tear-offs sean explícitos (#2399).

Creemos que estos cambios tienen un bajo impacto en el código migrado para usar null safety. Cuando lancemos la primera compilación alfa de Dart 3, podrás probar rápidamente si alguno de estos cambios rupturistas menores se aplica a tus paquetes o apps.

Nuevas características y capacidades de Dart 3

#

Dart 3 también se espera que contenga una serie de nuevas capacidades, incluyendo interoperabilidad mejorada con otros lenguajes de programación y nuevas características del lenguaje. Hablaremos mucho más sobre este tema en nuestro próximo evento importante, Flutter Forward el 25 de enero de 2023.

Echemos un vistazo previo a una característica del lenguaje, llamada patterns. Los patterns hacen que el lenguaje Dart sea mucho más expresivo, añaden soporte para datos más estructurados y permiten un estilo más funcional con tipos de datos algebraicos.

El siguiente código muestra un ejemplo de uso de múltiples valores de retorno en una función, junto con la capacidad de desestructurar estos en variables individuales:

dart
// A function which returns a record -- in this case a pair of two doubles:
(double x, double y) getLocation(String name) {
  if (name == 'Aarhus') {
    return (56.1629, 10.2039);
  } else {
    ...
  }
}

// Deconstructing the returned record back into individual variables:
void main(List<String> arguments) {
  final (lat, long) = getLocation('Aarhus');
  print('Current location: $lat, $long');
}

// You can also define a hierarchy of classes, and then pattern match on those:
sealed class Shape {
  double calculateArea();
}

class Square implements Shape {
  final double length;
  Square(this.length);
  double calculateArea() => length * length;
}

class Circle implements Shape {
  final double radius;
  ...
}

double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(radius: var r) => math.pi * r * r
};

Compartiremos muchos más detalles sobre esta nueva característica a principios del próximo año. Para ver un vistazo previo, puedes revisar la especificación del lenguaje

Más allá de Dart 3

#

Trabajamos en una multitud de posibles nuevas características en paralelo, moviéndonos de la ideación a la experimentación y al posible lanzamiento. Como consecuencia, algo del trabajo actual se completará después de Dart 3.

Primero, como mencionamos el año pasado, estamos trabajando en el soporte para compilar código Dart a WebAssembly (Wasm). Wasm permite que las apps de Flutter Web se ejecuten como código nativo completo en los navegadores. Esta es una gran tarea, requiere trabajo, más allá de actualizar los compiladores de Dart. Requiere colaborar con el W3C y los proveedores de navegadores en agregar soporte para lenguajes con recolección de basura en Wasm a través de la extensión WasmGC.

Segundo, estamos trabajando en macros. Estos permiten metaprogramación estática. Este poderoso mecanismo permite que un fragmento de código (un macro) modifique y extienda el código fuente de un programa durante la compilación del programa. Por ejemplo, esto puede reducir el código repetitivo necesario para deserializar JSON o crear clases de datos.

Debes esperar estas características, y otras emocionantes, después de Dart 3.

El camino hacia Dart 3

#

Como se mencionó en las secciones anteriores, si no has migrado a sound null safety, ese es el primer paso. ¡Recomendamos que lo hagas ahora!

A continuación, Dart 3 se lanzará en una serie de hitos. Nuestras expectativas actuales giran en torno a estas fechas:

  • Alrededor de enero/febrero de 2023: Se lanza la alfa de Dart 3. Se enfoca en habilitar pruebas tempranas de compatibilidad con Dart 3. Esperamos que puedas ejecutar análisis estático (dart analyze / flutter analyze). Es nuestro objetivo que cualquier app o paquete que pase el análisis estático con la alfa de Dart 3 debería soportar Dart 3 estable.

  • Alrededor de marzo/abril de 2023: Se lanza la beta de Dart 3. Esta versión presenta las nuevas características de Dart 3. Puedes usar esto para experimentar con las nuevas características y dar retroalimentación sobre problemas o sugerencias de mejoras.

  • Alrededor de mediados de 2023: Se lanza Dart 3 estable. Sound null safety se convierte en el único modo admitido.

Resumen

#

El lanzamiento de Dart 3 está programado para mediados de 2023. Contendrá varios cambios rupturistas, principalmente la discontinuación de ejecutar sin sound null safety. Esperamos tener una compilación alfa de Dart 3 lista alrededor de enero o febrero de 2023, que podrás usar para pruebas de compatibilidad con Dart 3.

Para preparar tus apps:

Dart 3 también contendrá varias nuevas características potentes, como patterns. Esperamos tener una versión beta de Dart 3 en la primavera que demuestre toda la nueva funcionalidad.

Más de Dart