Primero quiero decir que Java es el único lenguaje que he usado, así que disculpe mi ignorancia sobre este tema.
Los idiomas escritos dinámicamente le permiten poner cualquier valor en cualquier variable. Entonces, por ejemplo, podría escribir la siguiente función (psuedocode):
void makeItBark(dog){
dog.bark();
}
Y puedes pasar dentro de él cualquier valor. Mientras el valor tenga un bark()
método, el código se ejecutará. De lo contrario, se genera una excepción de tiempo de ejecución o algo similar. (Corrígeme si me equivoco sobre esto).
Aparentemente, esto te da flexibilidad.
Sin embargo, leí un poco sobre lenguajes dinámicos, y lo que la gente dice es que cuando diseñas o escribes código en un lenguaje dinámico, piensas en los tipos y los tienes en cuenta, tanto como lo harías en un lenguaje estáticamente escrito.
Entonces, por ejemplo, al escribir la makeItBark()
función, intenta que solo acepte 'cosas que pueden ladrar', y aún así necesita asegurarse de que solo le pase este tipo de cosas. La única diferencia es que ahora el compilador no le dirá cuándo cometió un error.
Claro, hay una ventaja de este enfoque que es que en los lenguajes estáticos, para lograr 'esta función acepta cualquier cosa que pueda ladrar', necesitaría implementar una Barker
interfaz explícita . Aún así, esto parece una pequeña ventaja.
¿Me estoy perdiendo de algo? ¿Qué estoy ganando realmente usando un lenguaje de tipo dinámico?
fuente
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
. Ese argumento ni siquiera es una clase , es una tupla anónima llamada. La escritura de pato ("si suena como un ...") le permite hacer interfaces ad hoc con esencialmente cero restricciones y sin sobrecarga sintáctica. Puedes hacer esto en un lenguaje como Java, pero terminas con mucha reflexión desordenada. Si una función en Java requiere una ArrayList y desea darle otro tipo de colección, es SOL. En python que ni siquiera puede aparecer.bark()
método, con el compilador quejándose cuando pasa algo incorrecto pero sin tener que declarar realmente una interfaz que contiene ladrido ().getMember
función personalizada ,makeItBark
explota porque llamó endog.bark
lugar dedog.getMember("bark")
. Lo que hace que el código funcione es que todos están implícitamente de acuerdo en usar el tipo de objeto nativo de Python.Just because I wrote makeItBark with my own types in mind doesn't mean you can't use yours, wheras in a static language it probably /does/ mean that.
Como señalé en mi respuesta, este no es el caso en general . Ese es el caso de Java y C #, pero esos lenguajes tienen sistemas de módulos y tipos paralizados, por lo que no son representativos de lo que puede hacer la escritura estática. Puedo escribir un genérico perfectamentemakeItBark
en varios lenguajes de tipo estático, incluso los no funcionales como C ++ o D.Respuestas:
Los idiomas de tipo dinámico son de tipo único
Comparando sistemas de tipos , no hay ventaja en la escritura dinámica. La escritura dinámica es un caso especial de escritura estática : es un lenguaje de escritura estática donde cada variable tiene el mismo tipo. Puede lograr lo mismo en Java (menos concisión) haciendo que cada variable sea de tipo
Object
y que los valores de "objeto" sean de tipoMap<String, Object>
:Por lo tanto, incluso sin reflexión, puede lograr el mismo efecto en casi cualquier lenguaje de tipo estático, dejando de lado la comodidad sintáctica. No obtienes ningún poder expresivo adicional; por el contrario, tiene menos poder expresivo porque en un lenguaje de tipo dinámico, se le niega la capacidad de restringir variables a ciertos tipos.
Hacer un ladrido de pato en un lenguaje estáticamente escrito
Además, un buen lenguaje de tipo estático le permitirá escribir código que funcione con cualquier tipo que tenga una
bark
operación. En Haskell, esta es una clase de tipo:Esto expresa la restricción de que para que algún tipo
a
se considere Barkable, debe existir unabark
función que tome un valor de ese tipo y no devuelva nada.Luego puede escribir funciones genéricas en términos de la
Barkable
restricción:Esto dice que
makeItBark
funcionará para cualquier tipo que satisfagaBarkable
los requisitos. Esto puede parecer similar a uninterface
en Java o C #, pero tiene una gran ventaja: los tipos no tienen que especificar por adelantado qué clases de tipos satisfacen. Puedo decir que el tipoDuck
esBarkable
en cualquier momento, incluso siDuck
es un tipo de terceros que no escribí. De hecho, no importa que el escritor deDuck
no haya escrito unabark
función; puedo proporcionarla después del hecho cuando digo el idioma queDuck
satisfaceBarkable
:Esto dice que
Duck
s puede ladrar, y su función de ladrido se implementa golpeando al pato antes de hacerlo graznar. Con eso fuera del camino, podemos llamarmakeItBark
a los patos.Standard ML
yOCaml
son aún más flexibles en el sentido de que puede satisfacer la misma clase de tipo en más de una forma. En estos idiomas puedo decir que los enteros se pueden ordenar usando el orden convencional y luego dar la vuelta y decir que también se pueden ordenar por divisibilidad (por ejemplo,10 > 5
porque 10 es divisible por 5). En Haskell solo puede crear una instancia de una clase de tipo una vez. (Esto le permite a Haskell saber automáticamente que está bien llamarbark
a un pato; en SML u OCaml debe ser explícito sobre québark
función desea, porque puede haber más de una).Concisión
Por supuesto, hay diferencias sintácticas. El código de Python que presentó es mucho más conciso que el equivalente de Java que escribí. En la práctica, esa concisión es una gran parte del atractivo de los lenguajes de tipo dinámico. Pero la inferencia de tipos le permite escribir código que sea igual de conciso en lenguajes tipados estáticamente, al liberarlo de tener que escribir explícitamente los tipos de cada variable. Un lenguaje de tipo estático también puede proporcionar soporte nativo para el tipeo dinámico, eliminando la verbosidad de todas las manipulaciones de fundición y mapas (por ejemplo, C # 's
dynamic
).Programas correctos pero mal escritos
Para ser justos, la escritura estática necesariamente excluye algunos programas que son técnicamente correctos aunque el verificador de tipos no pueda verificarlo. Por ejemplo:
La mayoría de los idiomas de tipo estático rechazarían esta
if
afirmación, aunque la rama else nunca ocurrirá. En la práctica, parece que nadie hace uso de este tipo de código; cualquier cosa demasiado inteligente para el verificador de tipos probablemente hará que los futuros mantenedores de su código lo maldigan a usted y a sus familiares. Por ejemplo, alguien tradujo con éxito 4 proyectos de Python de código abierto a Haskell, lo que significa que no estaban haciendo nada que un buen lenguaje de tipo estático no pudiera compilar. Además, el compilador encontró un par de errores relacionados con el tipo que las pruebas unitarias no detectaban.El argumento más fuerte que he visto para la tipificación dinámica son las macros de Lisp, ya que le permiten extender arbitrariamente la sintaxis del lenguaje. Sin embargo, Typed Racket es un dialecto de Lisp de tipo estático que tiene macros, por lo que parece que el tipeo estático y las macros no son mutuamente excluyentes, aunque tal vez sea más difícil de implementar simultáneamente.
Manzanas y naranjas
Finalmente, no olvide que hay diferencias más grandes en los idiomas que solo su sistema de tipos. Antes de Java 8, hacer cualquier tipo de programación funcional en Java era prácticamente imposible; una lambda simple requeriría 4 líneas de código de clase anónimo repetitivo. Java tampoco tiene soporte para literales de colección (por ejemplo
[1, 2, 3]
). También puede haber diferencias en la calidad y disponibilidad de herramientas (IDE, depuradores), bibliotecas y soporte comunitario. Cuando alguien afirma ser más productivo en Python o Ruby que Java, esa disparidad de características debe tenerse en cuenta. Hay una diferencia entre comparar idiomas con todas las baterías incluidas , núcleos de idiomas y sistemas de tipos .fuente
This is a difficult, and quite subjective issue. (And your question may get closed as opinion-based, but that doesn't mean it's a bad question - on the contrary, even thinking about such meta-language questions is a good sign - it's just not well-suited to the Q&A format of this forum.)
Here's my view of it: The point of high-level languages is to restrict what a programmer can do with the computer. This is surprising to many people, since they believe the purpose is to give users more power and achieve more. But since everything you write in Prolog, C++ or List is eventually executed as machine code, it is actually impossible to give the programmer more power than assembly language already provides.
The point of a high-level language is to help the programmer to understand the code they themselves have created better, and to make them more efficient at doing the same thing. A subroutine name is easier to remember than a hexadecimal address. An automatic argument counter is easier to use than a call sequence here you have to get the number of arguments exactly right on your own, with no help. A type system goes further and restricts the kind of arguments you can provide in a given place.
Here is where people's perception differs. Some people (I'm among them) think that as long as your password checking routine is going to expect exactly two arguments anyway, and always a string followed by a numeric id, it's useful to declare this in the code and be automatically reminded if you later forget to follow that rule. Outsourcing such small-scale book-keeping to the compiler helps free your mind for higher-level concerns and makes you better at designing and architecting your system. Therefore, type systems are a net win: they let the computer do what it's good at, and humans do what they're good at.
Others see to quite differently. They dislike being told by a compiler what to do. They dislike the extra up-front effort to decide on the type declaration and to type it. They prefer an exploratory programming style where you write actual business code without having a plan that would tell you exactly which types and arguments to use where. And for the style of programming they use, that may be quite true.
I'm oversimplifying dreadfully here, of course. Type checking is not strictly tied to explicit type declarations; there is also type inference. Programming with routines that actually do take arguments of varying types does allow quite different and very powerful things that would otherwise be impossible, it's just that a lot of people aren't attentive and consistent enough to use such leeway successfully.
In the end, the fact that such different languages are both very popular and show no signs of dying off shows you that people go about programming very differently. I think that programming language features are largely about human factors - what supports the human decision-making process better - and as long as people work very differently, the market will provide very different solutions simultaneously.
fuente
Code written using dynamic languages is not coupled to a static type system. Therefore, this lack of coupling is an advantage compared to poor/inadequate static type systems (although it may be a wash or a disadvantage compared to a great static type system).
Furthermore, for a dynamic language, a static type system doesn't have to be designed, implemented, tested, and maintained. This could make the implementation simpler compared to a language with a static type system.
fuente