http://sg.com.mx/sgce/2013/sessions/c%C3%B3mo-minimizar-las-desventajas-del-tipado-est%C3%A1tico-y-capitalizar-sus-ventajas
Esta será una conferencia técnica sobre lenguajes de programación donde Gavin King discutirá las ventajas y desventajas del tipado estático (static typing), mostrando cómo es que lenguajes modernos de programación como Ceylon ayudan a minimizar las desventajas al mismo tiempo que capitalizan las ventajas.
2. Aprender Ceylon
Taller Ceylon
El sabado a las 9:30 AM
Goldsmith #40 Col. Polanco,
entre Dickens y Emilio
Castelar
Con Gavin y Enrique
3. Acerca de Ceylon
• Nuevo lenguaje de programación
• Para máquinas virtuales
– JVM y JavaScript (el entorno)
• Plataforma modular
– “módulo del lenguaje”— mínimo, ligero
– otros módulos independientes del entorno
– módulos para un solo entorno (JVM, JavaScript)
4. Acerca de Ceylon
• Herramientas
– Ceylon IDE y línea de comando
• Infraestructura para manejar y compartir los
módulos
– arquitectura de repositorios de módulos y dependencias
– Ceylon Herd (un repo central para la comunidad)
– entorno de módulos (JBoss Modules/CommonJS)
5. Expresividad
• Es muy fácil diseñar un lenguaje muy expresivo
• Por ejemplo, con los lenguajes “dinámicos” se
siente como que se puede hacer casi lo que sea
• Pero con lenguajes así es muy difícil crear
herramientas que nos ayuden como programadores
a encontrar bugs, refactorizar y analizar el código
– el compilador
– el IDE
– el compilador de documentación
6. Expresividad
• Para programas grandes las herramientas pueden
mejorar mucho la productividad
• Por lo tanto necesitamos un lenguaje con reglas
– Estas reglas nos estorban para expresarnos!
– Pero cuando están bien diseñadas frecuentemente nos
ayudan a llegar a un diseño más elegante y más robusto
– Ademas, las herramientas y las reglas nos ayudan a
comprender el código horrible que han hecho nuestros
compañeros locos! (Gasto mucho mas tiempo en leer el
código de otros que en escribir mi propio código)
7. Limites de expresividad
• Siempre podemos aumentar la expresividad de un
lenguaje añadiendo características, introduciendo
nuevas reglas especiales, nueva sintaxis
• Al final llegamos a tener un lenguaje tan
complicado que el programador no sabe todas las
reglas, ni puede reproducir el razonamiento del
compilador, ni entiende el código de otros o de las
bibliotecas que utiliza
• También hay limites básicos de decidibilidad que
restringen el razonamiento del compilador
8. Objetos y tipos estáticos
• Un lenguaje orientado a objetos es un lenguaje
con polimorfismo de subtipos
• Cuando combinamos subtipos con un sistema de
tipos estáticos, llegamos muy rápido al problema
de tipos contenedores (colecciones)
• Para modelar el contrato de un tipo contenedor
hay que introducir polimorfismo paramétrico
(generics)
– Entonces encontramos las dos variedades de
polimorfismo en Java, C#, C++, Ceylon, Scala, ...
9. Objetos y tipos estáticos
• La mala noticia: un sistema de tipos con la
combinación de los dos polimorfismos es
necesariamente bastante complejo
– Abandonamos objetos? Abandonamos tipos estáticos?
No! Al final vale la pena
• En cambio, buscamos cómo presentarlo de manera
más digerible y eliminar la complejidad
innecesaria
– Qué ofrece la nueva generación de lenguajes con tipos
estáticos? Pues que son los puntos dolorosos?
10. Los puntos dolorosos
• Declarar tipos resulta en verbosidad
• Abstraer sobre tipos que no controlamos
– por ejemplo, tipos definidos en otros módulos que no
tienen ningún supertipo en común
• Varianza con wildcards
• Tratar con funciones como valores y abstraer sobre
tipos de funciones
– para definir funciones genéricas de orden superior
• El valor null y la maldita NullPointerException
11. Verbosidad
• Poner el tipo como int, bool, char no cuesta mucho,
pero ahora, con generics, tratamos con tipos como
Map<String,WeakRef<Object?>>
String|{Character*}
• Inferencia de tipos (alcance local)
value weakRefMap = Map { key1 -> WeakRef(val1),
key2 -> WeakRef(val2) };
• Aliases de tipos
alias WeakRefMap => Map<String,WeakRef<Object?>>;
alias Stringish => String|{Character*};
12. Uniones, intersecciones
• A veces encontramos casos como
– print() que acepta un String, Integer, o Float
– readLine() que produce un String, EOF o Error
– send() que acepta algo que es Persistent y
Serializable
• Malas soluciones:
– usar Object
– sobrecargar print()
– crear una clase intermedia (algun tipo de wrapper)
– En un lenguaje dinámico no necesitamos nada así
13. Uniones, intersecciones
• Mejor solución con tipos unión e intersección
– void print(String|Integer|Float line) { ... }
– String|EOF|Error readLine() { ... }
– void send(Persistent&Serializable message) { ... }
• De hecho, uniones e intersecciones nos ayudan
mucho a simplificar el sistema de tipos y hacerlo
más elegante y más completo
–Les van a encantar, lo prometo
14. Varianza
• Covarianza: intuitivamente un Set<Integer> es un
Set<Number>
– también tenemos contravarianza
• Mala solución: wildcards de Java
– el Set<Integer> es un Set<? extends Number>
– casi nadie los entiende completamente
– resultan en errores muy confusos
– bastante verboso
15. Varianza
• Mejor solución con varianza de lado de declaración
• Declaramos la varianza del tipo donde lo definimos
– covariante out, contravariante in
– interface Set<out T> { ... }
– el compilador verifica que el tipo sí es verdaderamente
covariante o contravariante
• Cuando utilizamos el tipo, todo funciona como
intuitivamente esperamos
– ahora Set<Integer> sí es un Set<Number>
16. Null y Nothing
• Socavamos el sistema entero cuando ponemos
hoyos para “conveniencia”
– NullPointerException es una excepción de tipo en
tiempo de ejecución como tienes con los tipos
dinámicos!
• El tipo bottom o Nothing es un subtipo de todo tipo
– Corresponde al conjunto vacío en matemáticas
– Entonces el tipo bottom no tiene ningún valor
– Excepto en Java, C#, Scala ... donde tiene el valor null
– Oops.
17. Null y Nothing
• En Ceylon, el tipo Nothing no tiene ningún valor
• Tenemos una clase Null perfectamente ordinaria
con un único valor null
– Escribimos “un string o null” como String|Null
– O lo abreviamos como String?
• El compilador verifica que cada valor ha sido
inicializado antes de ser utilizado
– para no encontrar valores no inicializados en tiempo de
ejecución
• Ninguna NullPointerException ocurre jamás!
18. Acotamiento de tipos
• Cómo podemos obtener un String si tenemos un
valor de tipo String?
– un caso especial del problema de acotamiento
• El tipo de un valor puede cambiar con el flujo de
código condicional
• Ya no necesitamos los casts inseguros de C
String? name = process.arguments[0];
if (exists name) {
//aquí, name es de tipo String
print(“Hello, “ + name + “!”);
}
19. Funciones y tuplas
• Cómo podemos representar el tipo de una tupla
por ejemplo [0.0, 0.0, “origen”]
• Luego, cómo representar el tipo de una función
– combina un tipo de tupla con otro tipo
• Definimos Tuple y Callable como clases ordinarias y
proveemos azúcar sintáctica
– tipo de tupla: [Float, Float, String]
– tipo de función: Point(Float, Float, String)
• Nos deja definir funciones de orden superior
incluso funciones genéricas como compose()
20. La mala noticia
• El sistema de inferencia de tipos funciona bien sin
ambigüedades gracias al concepto de tipos
principales
– Cada expresión tiene su único tipo más específico, su
tipo principal (frecuentemente expresado con el uso de
unión y intersección)
• Funciones sobrecargadas rompen el sistema de
tipos principales
– Una función sobrecargada no tiene ningún tipo principal
– Como los lenguajes dinámicos no tenemos overloading
21. Conclusión
• Se puede mejorar mucho el sistema de tipos,
aliviando los puntos dolorosos
• Un punto clave es ajustar el punto de vista para
pensar en tipos como conjuntos
• Así podemos tratar con tipos de una manera más
sofisticada, formando expresiones con | y &,
introduciendo aliases, infiriendo tipos complejos
• El problema de decidibilidad limita lo que
podemos hacer
22. Conclusión
• La nueva generación de lenguajes de tipado
estático es mucho más expresiva
• Puede ser tan expresivo y flexible como un
lenguaje dinámico? No!
– O, más bien, expresa cosas diferentes (tipos)
• Aún vale la pena — tipado estático tiene sus
propias ventajas, facilita herramientas!