Anunciando Dart 2.7: Un Dart más seguro y más expresivo
Hoy anunciamos el lanzamiento estable del SDK de Dart 2.7, con nuevas capacidades adicionales para desarrolladores. Ha sido un año ocupado para…
Hoy anunciamos la versión estable del SDK de Dart 2.7, con nuevas capacidades adicionales para desarrolladores. Ha sido un año ocupado para Dart, nuestro lenguaje optimizado para cliente para apps rápidas en cualquier plataforma. Hemos lanzado seis nuevas versiones con decenas de nuevas características. Ha sido muy gratificante ver a la comunidad de Dart usar estas características, y nos alegró el reciente informe de GitHub Octoverse que lista Dart como el lenguaje de más rápido crecimiento #1, clasificado por número de contribuyentes.
Dart 2.7 añade soporte para métodos de extensión, más un nuevo paquete para manejar cadenas con caracteres especiales. Tenemos una actualización sobre null safety (tipos nullable y non-nullable seguros) y una nueva experiencia de playground de null safety en DartPad. A nivel de ecosistema, pub.dev tiene una nueva característica de Like para dar feedback a los paquetes que aprecias. Dart 2.7 está disponible hoy como descarga de SDK desde dart.dev, y también está integrado en el lanzamiento de Flutter 1.12 de hoy.
Métodos de extensión
#Dart 2.7 añade una característica del lenguaje nueva y muy solicitada: métodos de extensión. Estos te permiten añadir nueva funcionalidad a cualquier tipo — incluso tipos que no controlas — y tener la brevedad y la experiencia de autocompletado de llamadas a métodos regulares.
Echemos un vistazo a un pequeño ejemplo: añadir soporte para analizar ints y doubles desde Strings. Como desarrolladores de apps no podemos cambiar la clase
String, porque está definida en la librería dart:core, ¡pero con métodos de extensión podemos extenderla! Una vez que definimos esta extensión, podemos llamar a nuestro nuevo método
parseInt en un String como si el método estuviera definido en la propia clase String:
extension ParseNumbers on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
main() {
int i = '42'.parseInt();
print(i);
}
Los métodos de extensión son estáticos
#Los métodos de extensión se resuelven y se despachan estáticamente, lo que significa que no puedes llamarlos en valores cuyo tipo es
dynamic. Aquí la llamada lanza una excepción en tiempo de ejecución:
dynamic d = '2';
d.parseInt();
→ Runtime exception: NoSuchMethodError
Los métodos de extensión funcionan bien con la inferencia de tipos de Dart, así que en la siguiente variable
v se infiere que tiene tipo String, y la extensión en String está disponible:
var v = '1';
v.parseInt(); // Works!
Como los métodos de extensión se resuelven estáticamente, son tan rápidos como llamar a una función estática o método helper, pero con una sintaxis de invocación mucho más amigable.
Las extensiones pueden tener variables de tipo
#Imagina que queremos definir una extensión en List para obtener los elementos en índices pares. Nos gustaría que esta extensión funcionara en listas de cualquier tipo, retornando una nueva lista del mismo tipo que la lista de entrada. Podemos hacer esto haciendo la extensión genérica y aplicando su parámetro de tipo tanto al tipo que extiende como al método de extensión:
extension FancyList<T> on List<T> {
List<T> get evenElements {
return <T>[for (int i = 0; i < this.length; i += 2) this[i]];
}
}
Los métodos de extensión son realmente miembros de extensión
#Llamamos a la característica métodos de extensión porque es terminología familiar si has usado la característica del lenguaje correspondiente en otros lenguajes de programación. Pero en Dart la característica es más general: también soporta extender clases con nuevos getters, setters y operadores. En el ejemplo de FancyList de arriba,
evenElements es un getter. Aquí hay un ejemplo de añadir un operador para desplazar cadenas:
extension ShiftString on String {
String operator <<(int shift) {
return this.substring(shift, this.length) + this.substring(0, shift);
}
}
Grandes ejemplos de la comunidad
#Ya hemos visto a muchos desarrolladores de la comunidad de Dart experimentar con métodos de extensión. Aquí hay algunos de los usos interesantes que hemos visto hasta ahora.
Jeremiah Ogbomo creó el paquete time, que usa extensiones en
num (la clase base para ints y doubles) para habilitar la construcción fácil de objetos Duration:
// Create a Duration via a `minutes` extension on num.
Duration tenMinutes = 10.minutes;
// Create a Duration via an `hours` extension on num.
Duration oneHourThirtyMinutes = 1.5.hours;
// Create a DateTime using a `+` operator extension on DateTime.
final DateTime afterTenMinutes = DateTime.now() + 10.minutes;
Marcelo Glasberg creó un paquete i18n (internacionalización) que usa métodos de extensión para simplificar la localización de cadenas:
Text('Hello'.i18n) // Displays Hello in English, Hola in Spanish, etc.
Simon Leier creó el paquete dartx, que contiene extensiones para un montón de tipos core de Dart. Algunos ejemplos:
var allButFirstAndLast = list.slice(1, -2); // [1, 2, 3, 4]
var notBlank = ' .'.isBlank; // false
var file = File('some/path/testFile.dart');
print(file.name); // testFile.dart
print(file.nameWithoutExtension); // testFile
Brian Egan está actualizando el popular paquete RxDart con métodos de extensión para redefinir el API para trabajar con streams.
Manejo seguro de substrings
#La clase String estándar de Dart usa la codificación UTF-16. Esta es una elección común en lenguajes de programación, especialmente aquellos que ofrecen soporte para ejecutarse tanto nativamente en dispositivos como en la web.
Las cadenas UTF-16 generalmente funcionan bien, y la codificación es transparente para el desarrollador. Sin embargo, al manipular cadenas, y especialmente al manipular cadenas ingresadas por usuarios, puedes experimentar una diferencia entre lo que el usuario percibe como un carácter y lo que está codificado como una unidad de código en UTF-16. Echemos un vistazo a un pequeño ejemplo, extrayendo los primeros tres caracteres de una cadena ingresada por un usuario:
var input = ['Resume'];
input.forEach((s) => print(s.substring(0, 3)));
$ dart main.dart
Res
No hay problemas hasta ahora; imprimimos los primeros tres caracteres de la cadena en nuestra lista de entrada, y el resultado es
Res. Ahora consideremos usuarios de diferentes regiones, que podrían ingresar cadenas que contienen acentos, Hangul (escritura coreana), e incluso una combinación de emoji para representar el concepto 'resume':
// New longer input list:
var input = ['Resume', 'Résumé', '이력서', '💼📃', 'Currículo'];
$ dart main.dart
Res
Ré
이력서
💼�
Cur
Hmm, algunos de esos funcionaron, pero ¿qué pasó con los elementos Résumé y 💼📃? Para
Résumé, ¿por qué obtuvimos una cadena de “dos caracteres”? Para 💼📃, ¿qué pasa con el signo de interrogación extraño? Los problemas aquí están en los
rincones oscuros de Unicode. La e acentuada
é en Résumé es en realidad dos code points: una e y un acento agudo combinante. Y 📃, el emoji de
página con curl, es un solo code point que resulta estar codificado con un par sustituto de
U+d83d U+dcc3. ¿Confundido?
Como dijimos, a menudo no necesitas preocuparte por los caracteres y code points. Si todo lo que haces es recibir, pasar y entregar cadenas completas, la codificación interna es transparente. Pero si necesitas iterar sobre los caracteres de una cadena o manipular el contenido de una cadena, puedes tener problemas. La buena noticia es que Dart 2.7 introduce un nuevo paquete, characters, para manejar estos casos. Este paquete soporta cadenas vistas como secuencias de caracteres percibidos por el usuario, también conocidos como grapheme clusters de Unicode. Con el paquete characters podemos corregir nuestro código con un pequeño cambio al código que acorta el texto:
// Before:
input.forEach((s) => print(s.substring(0, 3)));
// After, using the characters package:
input.forEach((s) => print(s.characters.take(3)));
Primero creamos una nueva instancia de Characters desde la cadena en s (usando el conveniente método de extensión
.characters). Luego usamos el método
take() para extraer los 3 caracteres iniciales.
Una vista previa técnica de este nuevo paquete está disponible en pub.dev. Nos encantaría escuchar tus opiniones sobre este paquete. Si encuentras algún problema, por favor repórtalo.
Vista previa de null safety
#Hace unos meses anunciamos nuestra intención de soportar null safety en Dart, añadiendo soporte para acceder a referencias de objetos de forma segura sin desencadenar excepciones de referencia nula. Hoy te damos una forma de previsualizar el análisis estático de null safety. Echemos un vistazo a un pequeño ejemplo motivador:
void main() {
Person('Larry', birthday: DateTime(1973, 03, 26)).describe();
Person('Sergey').describe();
}
class Person {
String firstName;
DateTime birthday;
Person(this.firstName, {this.birthday});
void describe() {
print(firstName);
int birthyear = birthday?.year;
print('Born ${DateTime.now().year - birthyear} years ago');
}
}
Si ejecutamos este código se crashea con una excepción de puntero nulo al describir la segunda persona, porque esa persona no tiene un cumpleaños establecido. Cometimos un error de programación: aunque anticipamos que algunas personas tendrían cumpleaños desconocidos haciendo el campo
birthday opcional en el constructor y verificando un cumpleaños nulo en birthday?.year, olvidamos manejar el caso donde
birthyear es null.
Intentemos pegar este código en nuestro nuevo playground de null safety, una versión especial de DartPad que contiene una vista previa técnica de la parte de análisis estático de la característica de null safety. Sin siquiera ejecutar el código, podemos ver tres problemas:
Al corregir estos errores de análisis, podemos empezar a aprovechar null safety. Intenta hacer las siguientes ediciones en el playground de null safety (eventualmente llegando a este código seguro):
-
Para declarar que birthday podría ser null, cambia
DateTime birthdayaDateTime? birthday -
Para declarar que birthyear podría ser null cuando birthday es null, cambia
int birthyearaint? birthyear -
Envuelve la última llamada a print en una verificación de null:
*if (birthyear != null)* {…}
Esperamos que este ejemplo te dé una buena idea de la experiencia que queremos con null safety. Como se mencionó, este playground es solo una vista previa técnica temprana de parte de null safety, ya que está siendo construida. Estamos trabajando duro en completar un primer lanzamiento beta de null safety en el SDK de Dart. Esto es lo que estamos trabajando para la beta:
Completar la implementación completa de referencias nullable y non-nullable
-
Integrar null safety en la inferencia de tipos y la promoción inteligente de Dart (por ejemplo, permitir acceso seguro a una variable nullable después de una asignación o verificación de null)
-
Portar las librerías core de Dart para declarar qué tipos son nullable y cuáles son non-nullable
-
Añadir una herramienta de migración, que puede automatizar la mayoría de las tareas de actualización para portar apps y paquetes de Dart
Una vez que este trabajo esté completo, lo haremos disponible en un SDK beta, y podrás empezar a aprovechar la característica en tus apps y paquetes. También planeamos mantener el playground de null safety actualizado con nuevas características a medida que se implementen.
Aunque estamos seguros de que muchos desarrolladores querrán usar null safety tan pronto como esté disponible, puedes migrar cuando te sea conveniente, optando por la característica cuando estés listo. Las librerías y paquetes que aún no han optado por la característica podrán depender de librerías que sí han optado, y viceversa.
Tendremos más que decir sobre null safety en los próximos meses, incluyendo consejos más detallados sobre cómo prepararse para la transición.
Dar Like 👍 a paquetes en pub.dev
#También se lanza hoy en pub.dev una nueva característica de Like para paquetes. Esto introduce una nueva “señal humana” para indicar qué paquetes te gustan. Para dar like a un paquete, simplemente haz clic en el icono de pulgar arriba que está junto a la información de detalle del paquete:
Actualmente no consideramos el número de likes en nuestro modelo de puntuación general, pero planeamos hacerlo en un lanzamiento posterior. También planeamos dar a nuestra interfaz de búsqueda general y páginas de lista una revisión visual que destacará la capacidad de recibir likes de un paquete.
Gracias
#En nombre del equipo de Dart, nos gustaría agradecerte — y a todos en la comunidad de Dart — por tu continuo apoyo. Por favor continúa ofreciéndonos comentarios y participa en las discusiones y comunidades de Dart. No seríamos un proyecto de código abierto que funcione bien sin el apoyo que recibimos de la comunidad de Dart.
2019 ha sido un año increíblemente emocionante para Dart, pero no nos detenemos aquí. Tenemos planes audaces para 2020, incluyendo lanzar versiones estables de características como dart:ffi y null safety e introducir nuevas características. Te invitamos a empezar a usar Dart 2.7 hoy. Está disponible en dart.dev, en el lanzamiento de Flutter 1.12 de hoy, y en el DartPad rediseñado recientemente.