No me gusta mucho la seguridad de tipos como concepto
Tal vez deberías estarlo.
para que el código escale bien, sea reutilizable, sea robusto, etc... tiene que ser de tipo seguro.
No creo que necesita para estar seguro del tipo, aunque seguro que ayuda. Mucha gente escribe código en C++, un lenguaje que tiene una seguridad de tipo relativamente débil (porque los punteros se pueden convertir a y desde enteros arbitrarios). Sin embargo, C++ también es un lenguaje que ha sido diseñado para fomentar la reutilización del código a través de código robusto encapsulado en clases.
Además, no hay que confundir la comprobación de tipos estática con la seguridad de tipos. Algunos argumentarían que los lenguajes con comprobación dinámica son igual de "seguros en cuanto a tipos"; simplemente hacen sus verificaciones del sistema de tipos en tiempo de ejecución en lugar de en tiempo de compilación.
Lenguajes como C# se toman en serio la seguridad de tipos
Sí, es cierto.
Los genéricos en C# son considerablemente más seguros que las plantillas de C++.
No veo ninguna prueba de esta afirmación. La comprobación de tipos en los genéricos es diferente a la comprobación de tipos en las plantillas, pero ambas son comprobadas. La diferencia fundamental es que un genérico asume que cualquier que cumple con las restricciones podría ser el argumento de tipo y, por lo tanto, requiere que el programa pase una comprobación de tipo estática para cualquier posible argumento de tipo. Por otro lado, las plantillas sólo requieren los argumentos de tipo que realmente se utiliza para pasar la comprobación de tipos estáticos.
Incluso ArrayList, que se considera que no es tan seguro como List<T>
siendo que es un List<object>
es mucho más seguro que, por ejemplo, un List<dynamic>
que creo que debería ser posible, pero es ciertamente oscuro.
Bueno, la dinámica es un caso interesante. Como dije antes, dinámico significa básicamente "trasladar la comprobación de tipos de esta cosa al tiempo de ejecución".
Me pregunto por qué, de nuevo usando C# como ejemplo sigue habiendo null. Puedo ver cómo en algunos aspectos es más rápido que asegurar que todo se construye por defecto, pero no es un gran problema de seguridad de tipos
Tienes razón al plantear esta preocupación; los nulos plantean un problema difícil al verificador de tipos. A uno le gustaría que un lenguaje de tipado estático fuera "seguro en memoria" - para asegurar que ningún patrón de bits inválido se abra paso en una variable anotada con un tipo particular. C# (fuera del subconjunto inseguro) logra este objetivo excepto que el patrón de bits de referencia nula siempre es legal en una variable anotada con un tipo de referencia, aunque ese patrón de bits no se refiera a ningún objeto válido.
Ciertamente, se pueden encontrar lenguajes que no tengan referencias nulas y que sean comprobados estáticamente; Haskell es un buen ejemplo. ¿Por qué no hacer lo mismo en C#?
Razones históricas. Las referencias nulas son muy útiles, a pesar de sus peligros, y C# proviene de una larga tradición de lenguajes de programación que permiten las referencias nulas.
Personalmente, habría preferido que hubiéramos incorporado la anulabilidad en el marco de trabajo desde el primer día, de modo que se pudieran tener tipos de valor anulables o no anulables, y tipos de referencia anulables o no anulables. Recuerda esto la próxima vez que diseñes un sistema de tipos desde cero.
También requiere una comprobación extra en tiempo de ejecución para que el ordenador lance excepciones nulas y demás
En realidad no es tan malo. La forma en que estas cosas se implementan normalmente es que la página de memoria virtual que contiene el puntero nulo se marca como no legible, escribible o ejecutable. Un intento de hacerlo resulta entonces en una excepción lanzada por el hardware. E incluso en los casos en los que hay que hacer una comprobación de nulos, comparar un registro a cero es bastante rápido.
Hay problemas peores. Por ejemplo, los arrays tampoco son "seguros" en el sentido de que no se puede comprobar estáticamente si un programa está garantizado para acceder sólo a los índices válidos de un Array. El CLR hace mucho trabajo para asegurarse de que cada acceso a un Array es válido.
La covarianza insegura de los arrays también es problemática; es un problema de buena fe del sistema de tipos. Tenemos que hacer comprobaciones de tipo cada vez que se escribe un elemento de un tipo más derivado a un Array de su tipo base. Este es mi candidato a "peor característica" del CLR.