Los lenguajes de tipo dinámico que conozco nunca permiten que los desarrolladores especifiquen los tipos de variables, o al menos tienen un soporte muy limitado para eso.
JavaScript, por ejemplo, no proporciona ningún mecanismo para imponer tipos de variables cuando sea conveniente hacerlo. PHP permite especificar algunos tipos de argumentos del método, pero no hay manera de utilizar los tipos nativos ( int
, string
, etc.) para los argumentos, y no hay manera de hacer cumplir tipos para otra cosa que argumentos.
Al mismo tiempo, sería conveniente tener la opción de especificar en algunos casos el tipo de una variable en un lenguaje de tipo dinámico, en lugar de hacer la verificación de tipo manualmente.
¿Por qué hay tal limitación? ¿Es por razones técnicas / de rendimiento (supongo que es en el caso de JavaScript), o solo por razones políticas (que es, creo, el caso de PHP)? ¿Es este el caso de otros idiomas de tipo dinámico con los que no estoy familiarizado?
Editar: siguiendo las respuestas y los comentarios, aquí hay un ejemplo para una aclaración: digamos que tenemos el siguiente método en PHP simple:
public function CreateProduct($name, $description, $price, $quantity)
{
// Check the arguments.
if (!is_string($name)) throw new Exception('The name argument is expected to be a string.');
if (!is_string($description)) throw new Exception('The description argument is expected to be a string.');
if (!is_float($price) || is_double($price)) throw new Exception('The price argument is expected to be a float or a double.');
if (!is_int($quantity)) throw new Exception('The quantity argument is expected to be an integer.');
if (!$name) throw new Exception('The name argument cannot be an empty string.');
if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
if ($price < 0) throw new Exception('The price argument cannot be less than zero.');
// We can finally begin to write the actual code.
// TODO: Implement the method here.
}
Con algunos esfuerzos, esto puede reescribirse como (ver también Programación por contratos en PHP ):
public function CreateProduct($name, $description, $price, $quantity)
{
Component::CheckArguments(__FILE__, __LINE__, array(
'name' => array('value' => $name, 'type' => VTYPE_STRING),
'description' => array('value' => $description, 'type' => VTYPE_STRING),
'price' => array('value' => $price, 'type' => VTYPE_FLOAT_OR_DOUBLE),
'quantity' => array('value' => $quantity, 'type' => VTYPE_INT)
));
if (!$name) throw new Exception('The name argument cannot be an empty string.');
if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
if ($price < 0) throw new Exception('The price argument cannot be less than zero.');
// We can finally begin to write the actual code.
// TODO: Implement the method here.
}
Pero el mismo método se escribiría de la siguiente manera si PHP aceptara opcionalmente tipos nativos para argumentos:
public function CreateProduct(string $name, string $description, double $price, int $quantity)
{
// Check the arguments.
if (!$name) throw new Exception('The name argument cannot be an empty string.');
if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
if ($price < 0) throw new Exception('The price argument cannot be less than zero.');
// We can finally begin to write the actual code.
// TODO: Implement the method here.
}
¿Cuál es más corto de escribir? ¿Cuál es más fácil de leer?
fuente
Respuestas:
El punto de tener una escritura estática es la capacidad de demostrar estáticamente que su programa es correcto con respecto a los tipos (nota: no es completamente correcto en todos los sentidos). Si tiene un sistema de tipo estático en todo momento, puede detectar errores de tipo la mayor parte del tiempo.
Si solo tiene información de tipo parcial, solo puede verificar las pequeñas partes de un gráfico de llamadas donde la información de tipo está completa. Pero ha dedicado tiempo y esfuerzo a especificar información de tipo para partes incompletas, donde no puede ayudarlo pero podría dar una falsa sensación de seguridad.
Para expresar información de tipo, necesita una parte del lenguaje que no puede ser excesivamente simple. Pronto se dará cuenta de que al igual que información
int
no es suficiente; querrá algo asíList<Pair<Int, String>>
, luego tipos paramétricos, etc. Puede ser bastante confuso incluso en el caso bastante simple de Java.Luego, necesitará manejar esta información durante la fase de traducción y la fase de ejecución, porque es una tontería verificar solo los errores estáticos; el usuario esperará que las restricciones de tipo siempre se mantengan si se especifica. Los lenguajes dinámicos no son demasiado rápidos como son, y tales comprobaciones reducirán aún más el rendimiento. Un lenguaje estático puede gastar un gran esfuerzo para verificar los tipos porque solo lo hace una vez; Un lenguaje dinámico no puede.
Ahora imagine agregar y mantener todo esto solo para que las personas a veces usen opcionalmente estas características, solo detectando una pequeña fracción de errores de tipo. No creo que valga la pena el esfuerzo.
El objetivo de los lenguajes dinámicos es tener un marco muy pequeño y muy maleable, dentro del cual pueda hacer cosas mucho más complicadas cuando se hace en un lenguaje estático: varias formas de parches de mono que se utilizan para metaprogramación, burlas y pruebas, reemplazo dinámico de código, etc. Smalltalk y Lisp, ambos muy dinámicos, lo llevaron al extremo de enviar imágenes de entorno en lugar de construir desde la fuente. Pero cuando desee asegurarse de que las rutas de datos particulares sean seguras para los tipos, agregue aserciones y escriba más pruebas unitarias.
fuente
En la mayoría de los lenguajes dinámicos, al menos puede probar dinámicamente el tipo de un objeto o valor.
Y hay inferenciadores de tipo estático, verificadores y / o ejecutores para algunos lenguajes dinámicos: por ejemplo
Y Perl 6 admitirá un sistema de tipos opcional con escritura estática.
Pero supongo que la conclusión es que mucha gente usa dinámicamente lenguajes porque se escriben dinámicamente, y para ellos el tipeo estático opcional es muy "aburrido". Y muchas otras personas los usan porque son "fáciles de usar para los que no son programadores", en gran parte como consecuencia de la naturaleza dinámica de la indulgencia. Para ellos, la escritura opcional es algo que no entenderán o no se molestarán en usar.
Si fueras cínico, podrías decir que la escritura estática opcional ofrece lo peor de ambos mundos. Para un fanático de tipo estático, no evita todas las fallas de tipo dinámico. Para un ventilador de tipo dinámico, sigue siendo una chaqueta recta ... aunque con las correas no apretadas.
fuente
Javascript planeó incluir algunos tipos de escritura estática opcionales, y parece que muchos lenguajes dinámicos maduros se dirigen de esa manera:
La razón es que cuando codifica por primera vez, desea ser rápido y dinámicamente escrito. Una vez que su código es sólido, funciona y tiene muchos usos (r), desea bloquear el diseño para reducir errores. (Esto es beneficioso tanto para los usuarios como para los desarrolladores, ya que los primeros verifican errores en sus llamadas y los segundos no rompen las cosas accidentalmente.
Tiene sentido para mí, ya que generalmente encuentro demasiada verificación de tipo al comienzo de un proyecto, muy poco al final de su vida útil, sin importar el idioma que use;).
fuente
Los objetos de Python hacen tener un tipo.
Usted especifica el tipo cuando crea el objeto.
En realidad, una verificación de tipo manual en Python es casi siempre una pérdida de tiempo y código.
Es simplemente una mala práctica escribir código de verificación de tipo en Python.
Si un sociópata malicioso utilizó un tipo inapropiado, los métodos ordinarios de Python generarán una excepción ordinaria cuando el tipo no sea apropiado.
No escribes ningún código, tu programa todavía falla con a
TypeError
.Hay casos muy raros en los que debe determinar el tipo en tiempo de ejecución.
Como no es una "limitación", la pregunta no es una pregunta real.
fuente
La mayoría de las veces, no es necesario, al menos no al nivel de detalle que sugiere. En PHP, los operadores que usa dejan perfectamente claro lo que espera que sean los argumentos; Sin embargo, es un poco un descuido del diseño que PHP arrojará sus valores si es posible, incluso cuando pasa una matriz a una operación que espera una cadena, y debido a que la conversión no siempre es significativa, a veces se obtienen resultados extraños ( y esto es exactamente donde los controles de tipo son útiles). Aparte de eso, no importa si agrega enteros
1
y /5
o cadenas"1"
y"5"
el mero hecho de que está utilizando+
El operador le indica a PHP que desea tratar los argumentos como números, y PHP obedecerá. Una situación interesante es cuando recibe resultados de consultas de MySQL: muchos valores numéricos simplemente se devuelven como cadenas, pero no lo notará, ya que PHP los convierte por usted cada vez que los trata como números.Python es un poco más estricto sobre sus tipos, pero a diferencia de PHP, Python ha tenido excepciones desde el principio y lo usa de manera consistente. El paradigma de "pedir perdón más fácil que permiso" sugiere simplemente realizar la operación sin verificación de tipo y confiar en que se genere una excepción cuando los tipos no tienen sentido. El único inconveniente de esto en lo que puedo pensar es que a veces, encontrarás que en algún lugar un tipo no coincide con lo que esperas que sea, pero encontrar la razón puede ser tedioso.
Y hay otra razón para considerar: los lenguajes dinámicos no tienen una etapa de compilación. Incluso si tiene restricciones de tipo, solo pueden dispararse en tiempo de ejecución, simplemente porque no hay tiempo de compilación . Si sus comprobaciones conducen a errores de tiempo de ejecución de todos modos, es mucho más fácil modelarlas en consecuencia: como comprobaciones explícitas (como
is_XXX()
en PHP otypeof
JavaScript), o lanzando excepciones (como lo hace Python). Funcionalmente, tiene el mismo efecto (un error se señala en tiempo de ejecución cuando falla una verificación de tipo), pero se integra mejor con el resto de la semántica del lenguaje. Simplemente no tiene sentido tratar los errores de tipo fundamentalmente diferentes de otros errores de tiempo de ejecución en un lenguaje dinámico.fuente
Puede que le interese Haskell: su sistema de tipos infiere los tipos del código y también puede especificarlos.
fuente
Como las otras respuestas han aludido, hay dos enfoques para escribir al implementar un lenguaje de programación.
Ambos enfoques son válidos, y cuál usar depende en parte de consideraciones técnicas como el rendimiento, y en parte de razones políticas como el mercado objetivo para el idioma.
fuente
En primer lugar, los lenguajes dinámicos se han creado principalmente para facilitar su uso. Como ha mencionado, es realmente bueno tomar la conversión de tipo automáticamente y proporcionarnos menos gastos generales. Pero al mismo tiempo carece de problemas de rendimiento.
Puede seguir con los lenguajes dinámicos, en el caso de que no se preocupe por el rendimiento. Digamos, por ejemplo, que JavaScript se ejecuta más lentamente cuando necesita realizar muchas conversiones de tipos en su programa, pero ayuda a reducir la cantidad de líneas en su código.
Y para mencionar, hay otros lenguajes dinámicos incluso que permiten al programador especificar el tipo. Por ejemplo, Groovy es uno de los famosos lenguajes dinámicos que se ejecuta en JVM. Y ha sido muy famoso incluso en los últimos días. Tenga en cuenta que el rendimiento de Groovy es el mismo que el de Java.
Espero que te ayude.
fuente
Simplemente no tiene sentido hacerlo.
¿Por qué?
Debido a que el sistema de tipos de DTL es precisamente tal que los tipos no se pueden determinar en tiempo de compilación. Por lo tanto, el compilador ni siquiera pudo verificar que el tipo especificado tendría sentido.
fuente
Eche un vistazo a Go, en la superficie está estáticamente tipado, pero esos tipos pueden ser interfaces que son esencialmente dinámicas.
fuente