Anunciando Dart 2.5: Desarrollo potenciado
Hoy anunciamos el lanzamiento estable del SDK de Dart 2.5, que incluye previews técnicos de dos grandes nuevas características para desarrolladores: ML…
Hoy anunciamos la versión estable del SDK de Dart 2.5, que incluye vistas previas técnicas de dos grandes nuevas características para desarrolladores:
ML Complete — completado de código potenciado por machine learning (ML) — y la interfaz de función externa dart:ffi para llamar código C directamente desde Dart. Dart 2.5 también tiene soporte mejorado para
expresiones constantes.
Este lanzamiento es otro paso hacia nuestra visión del mejor lenguaje optimizado para cliente para crear apps rápidas en cualquier plataforma. ML Complete es una poderosa adición a nuestro conjunto existente de herramientas de productividad como
hot reload, análisis estático personalizable y
Dart DevTools. La segunda característica en vista previa,
dart:ffi, te permite aprovechar APIs nativas existentes en los muchos sistemas operativos donde se ejecuta código Dart, así como librerías nativas multiplataforma existentes escritas en C.
Hablando de nuestra ambición de crear el mejor lenguaje optimizado para cliente, fue genial ver las nuevas calificaciones de IEEE Spectrum Top Programming Language 2019 publicadas esta semana con Dart ahora incluido. Dart entra en las calificaciones de lenguajes de programación de IEEE Spectrum como #16. También es #10 en tendencia, y #6 al filtrar a lenguajes para móvil (detrás de Java, C, C++, C# y Swift).
Vista previa: ML Complete, completados de código clasificados por machine learning
#Una de las principales ventajas de los lenguajes de programación tipados es que la información adicional capturada en los tipos permite al IDE/editor asistir a los desarrolladores ofreciendo completados mientras escriben su código. Con los completados de código, los desarrolladores pueden tanto evitar errores de escritura como explorar APIs escribiendo el inicio de los símbolos esperados y eligiendo entre los completados ofrecidos.
A medida que los APIs crecen, la exploración se vuelve difícil, ya que la lista de posibles completados se hace demasiado larga para navegar alfabéticamente. Hemos estado trabajando duro durante el último año para aplicar machine learning al problema. Simplificado, esto funciona entrenando un modelo de probabilidades de aparición de miembros basado en un contexto dado analizando un
gran corpus de código Dart
de código abierto en GitHub. Este modelo — potenciado por TensorFlow Lite
— puede luego usarse para predecir el probable siguiente símbolo mientras el desarrollador edita. Llamamos a esta nueva característica
ML Complete. Aquí hay un ejemplo de desarrollo de un nuevo widget MyHome usando el framework
Flutter:
Echemos un vistazo más profundo a cómo funciona esto. Imagina que estás escribiendo un pequeño programa para calcular el tiempo que es un día desde el tiempo actual. Usando ML Complete obtienes la experiencia de la izquierda, abajo. Primero, nota cómo puede ofrecer un completado para
DateTime.now() basándose en que la variable tiene el nombre now. Luego, nota cómo obtenemos un completado en el nombre de variable tomorrow, y finalmente
add(…) es el segundo completado ofrecido para now. En la experiencia sin ML Complete a la derecha, tenemos que empezar a escribir manualmente
DateTime, no obtenemos completados en el nombre de variable tomorrow, y el método
add(…)
está mucho más abajo en la lista.
Cómo probar ML Complete
#ML Complete se lanza hoy en vista previa. Está integrado directamente en el analyzer de Dart, por lo que está disponible en todos los editores compatibles con Dart, incluyendo Android Studio, IntelliJ y VS Code. Consulta esta página wiki para detalles sobre cómo participar en esta característica de vista previa, y para detalles sobre cómo proporcionar comentarios y reportar problemas.
Como la característica aún está en vista previa, la versión en los lanzamientos estables actuales de Flutter y Dart no incluye el trabajo de rendimiento y pulido que esperamos tener en versiones posteriores. Por lo tanto, recomendamos que temporalmente uses el canal dev de Flutter o un canal dev de Dart al probar esta característica.
Vista previa: interfaz de función externa dart:ffi para interop Dart-C
#
Muchos desarrolladores nos han pedido mejor soporte para llamar código C desde Dart. Una señal muy clara es el tracker de issues de Flutter, donde C interop es la solicitud de característica abierta mejor valorada con más de 600 votos 👍. Muchos casos de uso interesantes están detrás de estas solicitudes, desde llamar APIs de plataforma de bajo nivel como stdlib.h o Win32 hasta aprovechar librerías y utilidades multiplataforma existentes escritas en C como TensorFlow, Realm y SQLite.
Actualmente, el soporte para llamar C directamente desde Dart está limitado a una integración profunda en la Dart VM usando extensiones nativas. Alternativamente, las apps de Flutter pueden usar C indirectamente llamando al host mediante platform channels y llamando a C desde allí; esto es una doble indirección indeseable. Aspiramos a ofrecer un nuevo mecanismo que ofrezca excelente rendimiento, sea fácil de abordar, y funcione en los muchos plataformas y compiladores de Dart soportados.
El interop Dart-C habilita dos escenarios principales:
Llamar a un API de sistema basado en C en el sistema operativo host (OS)
Llamar a una librería basada en C, ya sea para un solo OS o multiplataforma
Llamar a APIs de sistema operativo basados en C
#Echemos un vistazo concreto a la primera categoría. Llamaremos al comando de Linux system. Este comando permite ejecutar cualquier comando del sistema; el argumento que le pasas es esencialmente pasado al shell/terminal y ejecutado allí. Aquí está el header C para este comando:
// C header: int system(const char *command) in stdlib.h
El desafío principal de cualquier mecanismo de interop es lidiar con las diferencias en semántica entre dos lenguajes. Para
dart:ffi, el código Dart necesita representar dos cosas:
La función C y los tipos de sus argumentos y tipo de retorno
La función Dart correspondiente, y sus tipos
Lo hacemos definiendo dos typedefs:
// C header typedef:
typedef SystemC = ffi.Int32 Function(ffi.Pointer<Utf8> command);
// Dart header typedef:
typedef SystemDart = int Function(ffi.Pointer<Utf8> command);
Luego necesitamos cargar la librería y buscar la función que vamos a llamar. Cómo hacer esto depende del sistema operativo; en este ejemplo estamos usando macOS. (Tenemos ejemplos completos para macOS, Windows y Linux.)
// Load `stdlib`. On MacOS this is in libSystem.dylib.
final dylib = ffi.DynamicLibrary.open('/usr/lib/libSystem.dylib');
// Look up the system function.
final systemP = dylib.lookupFunction<SystemC, SystemDart>('system');
Luego, codificamos el argumento de cadena usando la codificación relevante para el sistema operativo particular, invocamos la función, y liberamos la memoria del argumento nuevamente:
// Allocate a pointer to a Utf8 array containing our command.
final cmdP = Utf8.toUtf8('open http://dart.dev');
// Invoke the command.
systemP(cmdP);
// Free the pointer.
cmdP.free();
Este código ejecuta el comando del sistema, causando que el navegador por defecto del sistema abra dart.dev:
Llamar a frameworks y componentes basados en C
#Un segundo uso principal de dart:ffi es invocar frameworks y componentes basados en C. ¡El completado de código basado en ML del que hablamos al principio de esta publicación es un ejemplo concreto de esto! Usa
TensorFlow Lite, que es un API basado en C. Usar
dart:ffi nos permite ejecutar TensorFlow en todos los sistemas operativos donde necesitamos ofrecer completado de código, con el alto rendimiento de la implementación nativa de TensorFlow. Si quieres echar un vistazo al código de la integración de Dart con TensorFlow, consulta
este repo.
También esperamos que la capacidad de llamar a librerías basadas en C sea de gran utilidad para las apps de Flutter. Puedes imaginar llamar a librerías nativas como
Realm o SQLite, y creemos que
dart:ffi será valioso para habilitar plugins para Flutter desktop.
Envoltura de APIs y generación de código
#Como puedes haber notado, hay un poco de sobrecarga de programación en describir las funciones y buscar sus símbolos. Mucho de este código repetitivo podría generarse a partir de los archivos header de C. Actualmente estamos enfocados en proporcionar las primitivas subyacentes, pero nos encantaría colaborar con cualquiera que esté interesado en trabajar en un generador.
Cómo probar dart:ffi
#La librería dart:ffi se lanza hoy en vista previa. Como aún está en vista previa, recomendamos que uses el
canal master de Flutter o un
canal dev de Dart para obtener acceso rápido a nuestros cambios y mejoras a medida que los hacemos. Ten en cuenta que es probable que el API tenga cambios disruptivos entre ahora y su finalización, a medida que añadamos pulido y ampliamos el soporte para patrones comunes. Puedes obtener una vista detallada de lo que
tenemos planeado actualmente para nuestro primer lanzamiento. Aquí hay algunas limitaciones que debes conocer:
-
La librería no soporta structs anidadas, arrays inline, datos empaquetados, ni tipos primitivos dependientes de la plataforma.
-
El rendimiento de las operaciones de puntero es deficiente (pero se puede solucionar usando Pointer.asExternalTypedData).
-
La librería no tiene soporte para finalizadores (callbacks invocados cuando un objeto está a punto de ser recolectado por el garbage collector).
La documentación de C interop y la referencia del API de dart:ffi
documentan los conceptos principales y apuntan a ejemplos que puedes revisar. Si experimentas algún problema o tienes preguntas, te invitamos a publicar en el
grupo de discusión de Dart FFI, o reportar un issue.
Expresiones constantes mejoradas
#Dart ha soportado durante mucho tiempo la creación de variables y valores const; estos tienen la garantía de ser constantes en tiempo de compilación, y por lo tanto tienen muy buenas características de rendimiento. En versiones anteriores, las expresiones constantes eran un poco limitadas. A partir de Dart 2.5, soportamos muchas más formas de definir expresiones constantes, incluyendo la capacidad de usar casts y las nuevas características de control de flujo y spread de colecciones que se incluyeron en Dart 2.3:
// Example: these are now valid compile-time constants.
const Object i = 3;
const list = [i as int];
const set = {if (list is List<int>) ...list};
const map = {if (i is int) i: "int"};
Pensamientos finales
#Tenemos unos trimestres emocionantes por delante, con trabajo bien encaminado para métodos de extensión, forzar que las referencias sean non-nullable, y algo de planificación temprana para el lenguaje más allá de eso. También estamos investigando soporte de concurrencia mejorado — por ejemplo la capacidad de usar mejor procesadores multi-núcleo en teléfonos móviles modernos.
Estamos especialmente entusiastas acerca de non-nullable por defecto. Tenemos alineado un plan bastante ambicioso para esta característica, y tenemos mucho trabajo en curso. Algunos lenguajes más recientes fueron diseñados con soporte para non-nullable desde el principio, mientras que la mayoría de los lenguajes existentes que añadieron soporte para non-nullable en una versión posterior se conformaron con un enfoque bastante limitado, restringido a análisis estático adicional. Un diferenciador principal de nuestro enfoque es que apuntamos a soporte completo de non-nullable sound. Explicado brevemente, esto significa que nuestra comprensión de la no-nulabilidad se extenderá al núcleo del sistema de tipos, y una vez que nuestro sistema de tipos sepa que algo es non-nullable, podemos confiar plenamente en esa información, y nuestro compilador de back end es libre de optimizar el código. Esta solidez tiene grandes ventajas, tanto en términos de ofrecer una "experiencia sin excepciones" consistente como en términos de tamaño del código y rendimiento en tiempo de ejecución.
Siempre somos conscientes de la carga que supone para nuestro ecosistema cada vez que cambiamos el lenguaje. Por lo tanto, también estamos invirtiendo mucho en proporcionar herramientas de migración ricas para código existente. Esperamos que estas herramientas compensen la mayoría del costo de migración. También estamos añadiendo algunas características dedicadas del lenguaje y herramientas que permiten la migración por pasos, y haremos un esfuerzo por migrar tanto nuestro propio código como el código compartido en https://pub.dev.
¡Esperamos compartir más noticias más adelante este año!