Video Summary and Transcription
La charla analiza el potencial de los tipos de mayor orden (HKTs) para la semántica de bibliotecas, permitiendo una mejor inferencia y operaciones a nivel de tipos. Explica cómo se pueden crear HKTs en TypeScript aprovechando los tipos de función como una función de los atributos del objeto. Esto permite la creación de utilidades funcionales componibles con inferencia de tipos inteligente y una interfaz de encadenamiento que calcula resultados a nivel de tipos. En general, la charla destaca los beneficios y posibilidades de utilizar HKTs en el desarrollo de software.
1. Introducción a los Higher Kinda Types
Estoy emocionado de hablar sobre el potencial de los Higher Kinda Types para la semántica de las bibliotecas hoy. Los HKTs hacen posible una mejor inferencia. Podemos usar los HKTs para representar operaciones a nivel de tipos y componer tipos juntos utilizando semántica funcional. Los Higher Kinda Types son tipos asociados con un constructor de tipos. Queremos escribir código que aplique una operación a un mapa y luego la aplique a un array, extrayendo la lógica intrínseca de iterar sobre un array y aplicar una transformación a cada elemento.
¡Hola a todos! Estoy emocionado de hablar sobre el potencial de los Higher Kinda Types para la semántica de las bibliotecas hoy, como parte de la charla relámpago del Congreso de TypeScript 2023. Primero, un poco sobre mí. Mi nombre es Michael Potit. Soy gerente de ingeniería en Volley, donde estamos creando el futuro del entretenimiento habilitado para voz. Tengo pasión por la programación a nivel de tipos. He creado HKT Toolbelt, del cual hablaremos un poco más adelante. Pueden leer sobre mi programación en mi blog en code.lol. Y estoy emocionado de hablar sobre la flexibilidad de los Higher Kinda Types en TypeScript hoy. Entonces, semántica de bibliotecas. ¿Qué queremos decir con esto?
Queremos que el sistema de tipos infiera tanto como sea posible sobre nuestro código. A menudo, las bibliotecas no lo hacen. En este ejemplo de código, estamos usando Lodash. Estamos encadenando algunas operaciones en algún objeto. Estamos calculando las claves y luego mapeando esas claves para convertirlas todas en mayúsculas. Lo que sabemos que el valor debería ser es A, B y C en mayúsculas, pero Lodash solo infiere un array de strings. Esto a veces es muy incómodo y nos obliga a hacer declaraciones de tipos explícitas cuando de otra manera no tendríamos que hacerlo.
Entonces, los HKTs básicamente hacen posible una mejor inferencia. Podemos usar los HKTs para representar operaciones a nivel de tipos y podemos componer tipos juntos utilizando semántica funcional. Podemos hacer esto tan bien que los usuarios finales nunca tienen que escribir ningún tipo personalizado ellos mismos. Entonces, ¿qué son los Higher Kinda Types?
Básicamente, un kind es un tipo asociado con un constructor de tipos. En TypeScript, esto sería como el tipo foo de T es igual a T, ¿verdad? El kind más simple es simplemente star, que son tus tipos base. Y el kind siguiente más simple es star a star, que es tu tipo genérico de aridad uno normal. Entonces, capitalize de S o promise de T, etc. TypeScript admite nativamente estos dos, pero queremos más. Por ejemplo, nos gustaría admitir un tipo que reciba un tipo, genérico en sí mismo, y un valor que represente un array, y queremos iterar sobre ese array aplicando esta operación de tipo a cada elemento. Entonces, el kind asociado con esto sería esta expresión complicada, para la cual necesitamos hacer cosas inteligentes para admitirla en el sistema de TypeScript.
Entonces, ¿qué estamos tratando de hacer realmente? Lo que queremos hacer es escribir código como este, queremos decir que queremos usar este operador, o cualquier tipo de operador de aplicación, y decir, ah, sí. Queremos aplicar la operación capitalize a map, para que obtengamos map capitalize, que convierte capitalize en algo que se puede mapear sobre un array, y luego queremos aplicarlo a un array para obtener un resultado. Estamos extrayendo la lógica intrínseca de iterar sobre un array y aplicar una transformación a cada elemento.
2. Creando Higher Kind of Types en TypeScript
Esto es sorprendentemente posible en TypeScript. Explotamos el hecho de que los tipos de función pueden ser una función de los atributos del objeto. Podemos crear funciones que se comporten como higher kind of types. Usando esto, podemos crear utilidades funcionales componibles con inferencia de tipos inteligente. También podemos lograr una interfaz encadenada que calcula resultados a nivel de tipos a medida que se encadenan las operaciones. Esa es mi charla relámpago. Gracias por escuchar.
Esto es sorprendentemente posible en TypeScript. Entonces, aquí está la definición mínima necesaria para crear este rico conjunto de higher kind of types. Básicamente, lo que está sucediendo aquí es que estamos explotando el hecho de que en las interfaces, los tipos de funciones pueden ser una función de los atributos del objeto. Entonces, básicamente, lo que estamos haciendo es llenar el parámetro representado por el elemento x en la línea dos y luego extraer el tipo de retorno de f. Como vimos antes, si aplicamos, por ejemplo, la función de identidad a uno, obtendríamos simplemente uno. Aquí hay un ejemplo del tipo de higher kind de identidad. Decimos que identity extiende kind y luego devolvemos el tipo de x para la transformación subyacente que estamos representando. Siempre la transformación está en el lado derecho. Este es un ejemplo un poco más complicado. No necesitamos entender o recorrer todo esto, pero básicamente en la línea 11 aquí, estamos aplicando append a esta cadena bar, lo que crea una función que agrega la cadena bar a cualquier cadena que se le dé, y luego pasamos foo para obtener foo bar. Lo que notarán es que todos los argumentos están currificados en esta nueva semántica. También podemos hacer map, como mencioné antes. Nuevamente, no necesitamos entender toda la lógica aquí, pero notablemente lo que estamos haciendo es tomar esta función append que hicimos antes, convertirla en algo que se pueda mapear sobre un array y luego ejecutarla sobre un array para obtener foo bar y baz bar. Bueno, eso fue mucho código. La pregunta sería, ¿cómo lo usamos realmente en la programación real? Para hacer esto, en general, queremos usar un proceso que llamo ratificación de kind, pero básicamente estamos bajando un higher kind of type y le estamos dando una interfaz callable, por lo que en la línea dos decimos que la función callable va a ser una función genérica que es una entrada del tipo asociado con este tipo. Inferimos qué constante literal va a ser y luego ejecutamos recursivamente eso en todos los argumentos que este higher kind of type está devolviendo. No incluyo la implementación completa aquí, pero no es mucho más complicada que esto. Y básicamente lo que esto nos permite hacer es crear funciones que se comporten como higher kind of types. Entonces, usando esto, podemos crear esta vista de mapa reificada que incluye la implementación. Tenemos un mapa aquí, una lógica de mapa currificada muy simple, y luego decimos como mapa reificado y luego tenemos append que es simplemente como append reificado y luego podemos crear esta expresión de resultado donde estamos mapeando sobre un array con esta declaración de append para que obtengamos hello exclamation mark world exclamation mark. Entonces hemos creado estas utilidades funcionales componibles que tienen inferencia de tipos inteligente asociada.
Hasta ahora hemos utilizado la semántica point free para escribir nuestro código, donde no mencionamos explícitamente ningún argumento en nuestras funciones, pero a menudo las personas usan una API fluida, por ejemplo en lodash, con su semántica de encadenamiento, por lo que podemos hacer eso usando HKTs sin incluir la implementación completa, pero lo que queremos al final y lo que podemos obtener es una interfaz encadenada que realmente calcula el resultado a nivel de tipos a medida que se ejecutan y se encadenan estas operaciones. Entonces, lo que inferimos al final es A y B y C. Esa es mi charla relámpago. Muchas gracias por escuchar. Por favor, salgan y mejoren el ecosistema de TypeScript escribiendo bibliotecas que usen higher kind of types.
Comments