¿Por qué los idiomas escritos dinámicamente no permiten que el desarrollador especifique el tipo?

14

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 es conveniente hacerlo. PHP le permite especificar algunos tipos de argumentos de método, pero no hay manera de usar tipos nativos ( int , string , etc.) para los argumentos, y no hay manera de imponer tipos para otra cosa que no sean 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 realizar 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 un caso para 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 se puede reescribir como (también vea 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 opcionalmente aceptaría 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 para escribir? ¿Cuál es más fácil de leer?

    
pregunta Arseni Mourzenko 09.05.2011 - 12:54

10 respuestas

17

El punto de tener escritura estática es la capacidad de probar de manera estática 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 completo, puede detectar errores de tipo la mayor parte del tiempo.

Si solo tiene información de tipo parcial, solo puede verificar los fragmentos pequeños de un gráfico de llamadas donde la información de tipo está completa. Pero ha dedicado tiempo y esfuerzo a especificar la información de tipo para las 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 descubrirás que la información como int no es suficiente; querrá algo como List<Pair<Int, String>> , luego tipos paramétricos, etc. Puede ser lo suficientemente confuso incluso en el caso bastante simple de Java.

Luego, deberá manejar esta información durante la fase de traducción y en la fase de ejecución, ya que es una tontería verificar solo los errores estáticos; el usuario esperará que las restricciones de tipo siempre se mantengan, si se especifican. Los lenguajes dinámicos no son tan rápidos como son, y tales comprobaciones reducirán aún más el rendimiento. Un lenguaje estático puede dedicar un esfuerzo serio a la comprobación de 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 opcionalmente usen estas funciones, solo detectan una pequeña fracción de los errores de tipo. No creo que valga la pena el esfuerzo.

El punto central de los lenguajes dinámicos es tener un marco muy pequeño y muy maleable, dentro del cual puede hacer cosas que son mucho más complicadas cuando se hace en un lenguaje estático: varias formas de parches de monos que se usan para la metaprogramación. , burlas y pruebas, reemplazo dinámico de código, etc. Smalltalk y Lisp, ambos muy dinámicos, lo llevaron a un extremo como para 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 el tipo, agregue afirmaciones y escriba más pruebas unitarias.

    
respondido por el 9000 09.05.2011 - 14:27
8

En la mayoría de los idiomas dinámicos, puede al menos dinámicamente probar el tipo de un objeto o valor.

Y hay inferencers, verificadores y / o ejecutores de tipo estático para algunos lenguajes dinámicos: por ejemplo,

Y Perl 6 admitirá un opcional sistema de tipo con escritura estática.

Pero supongo que la conclusión es que muchas personas usan dinámicamente lenguajes porque son tipificados dinámicamente, y para ellos la tipificación estática opcional es muy "ho hum". Y muchas otras personas los usan porque son "fáciles de usar para los no programadores", en gran parte como consecuencia de la naturaleza dinámica de escritura indiscreta. Para ellos, la escritura opcional es algo que o no entenderán o no se molestarán en utilizar.

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 ajustadas.

    
respondido por el Stephen C 09.05.2011 - 13:04
2

Javascript planeaba incluir algunos tipos de escritura estática opcional, y parece que muchos lenguajes dinámicos maduros se están dirigiendo de esa manera-

La razón es que cuando codificas por primera vez, quieres ser rápido y tipeado dinámicamente. Una vez que su código es sólido, funciona y tiene muchos usos (r), desea bloquear el diseño para reducir los errores. (Esto es beneficioso tanto para los usuarios como para los desarrolladores, ya que los primeros recibirán una verificación de errores en sus llamadas y los últimos no romperán las cosas accidentalmente.

Para mí tiene sentido, ya que generalmente encuentro demasiados controles de tipo al inicio de un proyecto, muy poco al final de su vida útil, sin importar qué idioma use;).

    
respondido por el Macke 09.05.2011 - 13:56
2

Los objetos de Python tienen tienen un tipo.

Usted especifica el tipo cuando crea el objeto.

  

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 realizar la verificación de tipo manualmente.

En realidad, una comprobació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 comprobación de tipos en Python.

Si un sociópata malintencionado usaba un tipo inapropiado, los métodos ordinarios de Python generarán una excepción común cuando el tipo no sea apropiado.

No escribes ningún código, tu programa aún falla con un TypeError .

Hay casos muy raros en los que debe determinar el tipo en tiempo de ejecución.

  

¿Por qué hay tal limitación?

Dado que no es una "limitación", la pregunta no es una pregunta real.

    
respondido por el S.Lott 09.05.2011 - 21:04
2

La mayoría de las veces, no es necesario, al menos no al nivel de detalle que está sugiriendo. En PHP, los operadores que usas dejan perfectamente en claro cuáles esperas que sean los argumentos; es un poco de una supervisión de diseño, aunque PHP emitirá sus valores si es posible, incluso cuando pasa una matriz a una operación que espera una cadena, y como la conversión no siempre es significativa, a veces se obtienen resultados extraños ( y aquí es exactamente donde las comprobaciones de tipo son útiles). Aparte de eso, no importa si agrega enteros 1 y 5 o cadenas "1" y "5" : el simple hecho de que esté usando las señales del operador + para PHP que desee tratar. Los argumentos como números, y PHP obedecerán. 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 lanza por usted siempre que los trata como números.

Python es un poco más estricto con respecto a sus tipos, pero a diferencia de PHP, Python ha tenido excepciones desde el principio y lo usa de manera consistente. El paradigma "más fácil de pedir perdón que permiso" sugiere que solo se realice la operación sin verificación de tipo, y se base en que se genere una excepción cuando los tipos no tienen sentido. El único inconveniente de esto 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 activarse en tiempo de ejecución, simplemente porque no hay tiempo de compilación . Si sus cheques conducen a errores de tiempo de ejecución de todos modos, es mucho más fácil modelarlos en consecuencia: como cheques explícitos (como is_XXX() en PHP o typeof en javascript), o lanzando excepciones (como 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 idioma. Simplemente no tiene sentido tratar los errores de tipo fundamentalmente diferentes de otros errores de tiempo de ejecución en un lenguaje dinámico.

    
respondido por el tdammers 04.08.2011 - 21:53
0

Es posible que esté interesado en Haskell: su sistema de tipos infiere los tipos del código y también puede especificar tipos.

    
respondido por el daven11 09.05.2011 - 15:43
0

Como se ha aludido a las otras respuestas, existen dos enfoques para escribir al implementar un lenguaje de programación.

  1. Haga que el programador le diga qué usan todas las variables y funciones para los tipos. Idealmente, usted también verifica que las especificaciones de tipo sean precisas. Luego, debido a que sabe qué tipo de cosa habrá en cada lugar, puede escribir código que asuma que la cosa adecuada estará allí y utiliza la estructura de datos que use para implementar ese tipo directamente.
  2. Adjunte un indicador de tipo a los valores que se almacenarán en variables y que se pasarán y devolverán desde las funciones. Esto significa que el programador no tendrá que especificar ningún tipo para variables o funciones, porque los tipos realmente pertenecen a los objetos a los que se refieren las variables y funciones.

Ambos enfoques son válidos y su uso depende en parte de consideraciones técnicas como el desempeño y en parte de razones políticas como el mercado objetivo para el idioma.

    
respondido por el Larry Coleman 09.05.2011 - 22:19
0

En primer lugar, los lenguajes dinámicos se crean principalmente para la facilidad de uso. Como ha mencionado, es realmente bueno tomar la conversión de tipos automáticamente y proporcionarnos menos gastos generales. Pero al mismo tiempo carece de problemas de rendimiento.

Puedes seguir con los idiomas dinámicos, en el caso de que no te preocupes por el rendimiento. Por ejemplo, JavaScript se ejecuta más lentamente cuando necesita realizar muchos tipos de conversión en su programa, pero ayuda a reducir el número de líneas en su código.

Y para mencionar, hay otros lenguajes dinámicos que permiten al programador especificar el tipo. Por ejemplo, Groovy es uno de los famosos lenguajes dinámicos que se ejecutan 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.

    
respondido por el Ant's 10.05.2011 - 05:29
-1

Simplemente no tiene sentido hacerlo.

¿Por qué?

Porque el sistema de tipos de DTL es precisamente tal que los tipos no se pueden determinar en el momento de la compilación. Por lo tanto, el compilador ni siquiera pudo comprobar que el tipo especificado tendría sentido.

    
respondido por el Ingo 09.05.2011 - 13:47
-1

Eche un vistazo a Go, en la superficie está tipificado de manera estática, pero esos tipos pueden ser interfaces que son esencialmente dinámicas.

    
respondido por el dan_waterworth 05.08.2011 - 10:34

Lea otras preguntas en las etiquetas