Creo que entiendo la escritura fuerte , pero cada vez que busco ejemplos de lo que es débil, termino encontrando ejemplos de lenguajes de programación que simplemente coaccionan / convierten tipos automáticamente.
Por ejemplo, en este artículo llamado Typing: Strong vs. Weak, Static vs. Dynamic dice que Python está fuertemente tipado porque obtienes una excepción si intentas:
Pitón
1 + "1"
Traceback (most recent call last):
File "", line 1, in ?
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Sin embargo, tal cosa es posible en Java y en C #, y no los consideramos débilmente escritos solo por eso.
Java
int a = 10;
String b = "b";
String result = a + b;
System.out.println(result);
C#
int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);
En este otro artículo llamado Weakly Type Languages, el autor dice que Perl está tipeado débilmente simplemente porque puedo concatenar una cadena a un número y viceversa sin ninguna conversión explícita.
Perl
$a=10;
$b="a";
$c=$a.$b;
print $c; #10a
Entonces, el mismo ejemplo hace que Perl tenga un tipo débil, pero no Java y C # ?.
Vaya, esto es confuso
Los autores parecen implicar que un lenguaje que impide la aplicación de ciertas operaciones en valores de diferentes tipos está fuertemente tipado y lo contrario significa débilmente tipado.
Por lo tanto, en algún momento me sentí incitado a creer que si un idioma proporciona muchas conversiones automáticas o coerción entre tipos (como perl) puede terminar siendo considerado débilmente escrito, mientras que otros idiomas que proporcionan solo unas pocas conversiones pueden terminar siendo considerado fuertemente tipado.
Sin embargo, me inclino a creer que debo estar equivocado en esta interpretación, pero no sé por qué ni cómo explicarlo.
Entonces, mis preguntas son:
- ¿Qué significa realmente que un idioma se tipee realmente débilmente?
- ¿Podría mencionar algún buen ejemplo de tipeo débil que no esté relacionado con la conversión automática / coerción automática realizada por el idioma?
- ¿Se puede escribir un idioma débilmente y al mismo tiempo?
Respuestas:
ACTUALIZACIÓN: Esta pregunta fue el tema de mi blog el 15 de octubre de 2012. ¡ Gracias por la gran pregunta!
Significa "este lenguaje usa un sistema de tipos que me parece desagradable". Un lenguaje "fuertemente tipado", por el contrario, es un lenguaje con un sistema de tipos que me parece agradable.
Los términos son esencialmente sin sentido y debe evitarlos. Wikipedia enumera once significados diferentes para "fuertemente tipado", varios de los cuales son contradictorios. Esto indica que las posibilidades de crear confusión son altas en cualquier conversación que implique el término "fuertemente tipado" o "débilmente tipado".
Todo lo que realmente se puede decir con certeza es que un lenguaje "fuertemente tipado" en discusión tiene alguna restricción adicional en el sistema de tipos, ya sea en tiempo de ejecución o en tiempo de compilación, que carece de un lenguaje "tipeado débilmente" en discusión. Lo que podría ser esa restricción no se puede determinar sin más contexto.
En lugar de utilizar "fuertemente tipeado" y "débilmente tipado", debe describir en detalle a qué tipo de seguridad de tipo se refiere. Por ejemplo, C # es un lenguaje de tipo estático y un lenguaje seguro de tipo y un lenguaje seguro de memoria , en su mayor parte. C # permite que se violen las tres formas de escritura "fuerte". El operador de conversión viola la escritura estática; le dice al compilador "Sé más sobre el tipo de tiempo de ejecución de esta expresión que tú". Si el desarrollador está equivocado, el tiempo de ejecución arrojará una excepción para proteger la seguridad de tipo. Si el desarrollador desea romper la seguridad de tipo o la seguridad de la memoria, puede hacerlo apagando el sistema de seguridad de tipo haciendo un bloque "inseguro". En un bloque inseguro, puede usar la magia de puntero para tratar un int como un flotante (violando la seguridad de tipo) o para escribir en la memoria que no posee. (Violando la seguridad de la memoria).
C # impone restricciones de tipo que se verifican tanto en tiempo de compilación como en tiempo de ejecución, lo que lo convierte en un lenguaje "fuertemente tipado" en comparación con los idiomas que realizan menos verificación en tiempo de compilación o menos tiempo de ejecución. C # también le permite, en circunstancias especiales, ejecutar una ejecución final en torno a esas restricciones, lo que lo convierte en un lenguaje "débilmente tipado" en comparación con los idiomas que no le permiten realizar dicha ejecución final.
¿Cuál es realmente? Es imposible decirlo; depende del punto de vista del hablante y su actitud hacia las diversas características del lenguaje.
fuente
Como otros han señalado, los términos "fuertemente tipado" y "débilmente tipado" tienen tantos significados diferentes que no hay una respuesta única para su pregunta. Sin embargo, como mencionó específicamente a Perl en su pregunta, permítame tratar de explicarle en qué sentido Perl está tipeado débilmente.
El punto es que, en Perl, no existe una "variable entera", una "variable flotante", una "variable de cadena" o una "variable booleana". De hecho, por lo que el usuario puede (por lo general) decir, no son aún entero, flotante, cuerda o booleanas valores : todo lo que tienes son "escalares", que son todas estas cosas al mismo tiempo. Entonces puedes, por ejemplo, escribir:
Por supuesto, como notará correctamente, todo esto puede verse como solo coerción de tipo. Pero el punto es que, en Perl, los tipos siempre están obligados. De hecho, es bastante difícil para un usuario decir cuál es el "tipo" interno de una variable: en la línea 2 en mi ejemplo anterior, preguntando si el valor de
$bar
es la cadena"9"
o el número no9
tiene sentido, ya que, como en lo que respecta a Perl, son lo mismo . De hecho, es incluso posible que un escalar a Perl tiene internamente tanto una cadena y un valor numérico al mismo tiempo, como es por ejemplo el caso para$foo
después de la línea 2 anterior.La otra cara de todo esto es que, dado que las variables de Perl no están tipificadas (o, más bien, no exponen su tipo interno al usuario), los operadores no pueden sobrecargarse para hacer cosas diferentes para diferentes tipos de argumentos; no puede simplemente decir "este operador hará X para números e Y para cadenas", porque el operador no puede (no quiere) decir qué tipo de valores son sus argumentos.
Así, por ejemplo, Perl tiene y necesita un operador de suma numérica (
+
) y un operador de concatenación de cadenas (.
): como viste anteriormente, está perfectamente bien agregar cadenas ("1" + "2" == "3"
) o concatenar números (1 . 2 == 12
). Del mismo modo, los operadores de comparación numéricos==
,!=
,<
,>
,<=
,>=
y<=>
comparar los valores numéricos de sus argumentos, mientras que los operadores de comparación de cadenaseq
,ne
,lt
,gt
,le
,ge
ycmp
los comparan lexicográfico como cadenas. Entonces2 < 10
, pero2 gt 10
(pero"02" lt 10
, mientras"02" == 2
). (Eso sí, cierto otros idiomas, como JavaScript, intenta acomodar la escritura débil similar a Perl mientrastambién haciendo sobrecarga del operador. Esto a menudo conduce a la fealdad, como la pérdida de asociatividad para+
).(La mosca en el ungüento aquí es que, por razones históricas, Perl 5 tiene algunos casos de esquina, como los operadores lógicos bit a bit, cuyo comportamiento depende de la representación interna de sus argumentos. En general, se consideran un defecto de diseño molesto, ya que la representación interna puede cambiar por razones sorprendentes y, por lo tanto, predecir lo que hacen esos operadores en una situación determinada puede ser complicado).
Dicho todo esto, se podría argumentar que Perl hace tener tipos fuertes; simplemente no son el tipo de tipos que podrías esperar. Específicamente, además del tipo "escalar" discutido anteriormente, Perl también tiene dos tipos estructurados: "matriz" y "hash". Esos son muy distintos de los escalares, hasta el punto en que las variables de Perl tienen diferentes sigilos que indican su tipo (
$
para escalares,@
para matrices,%
para hashes) 1 . No son reglas de conversión entre estos tipos, por lo que puede escribir por ejemplo%foo = @bar
, pero muchos de ellos son bastante con pérdidas: por ejemplo,$foo = @bar
asigna la longitud de la matriz@bar
de$foo
, no su contenido. (Además, hay algunos otros tipos extraños, como los globos tipográficos y las manijas de E / S, que a menudo no se ven expuestos).Además, una pequeña grieta en este diseño agradable es la existencia de tipos de referencia, que son un tipo especial de escalares (y que se pueden distinguir de los escalares normales, utilizando el
ref
operador). Es posible usar referencias como escalares normales, pero sus valores de cadena / numéricos no son particularmente útiles, y tienden a perder su referencia especial si los modifica utilizando operaciones escalares normales. Además, cualquier variable Perl 2 se puedebless
editar en una clase, convirtiéndola en un objeto de esa clase; el sistema de clases OO en Perl es algo ortogonal al sistema de tipo primitivo (o falta de tipo) descrito anteriormente, aunque también es "débil" en el sentido de seguir la escritura del patoparadigma. La opinión general es que, si te encuentras comprobando la clase de un objeto en Perl, estás haciendo algo mal.1 En realidad, el sigilo denota el tipo de valor al que se accede, de modo que, por ejemplo,
@foo
se denota el primer escalar de la matriz$foo[0]
. Ver perlfaq4 para más detalles.A los objetos en Perl se accede (normalmente) a través de referencias a ellos, pero lo que realmente se
bless
edita es la variable (posiblemente anónima) a la que apunta la referencia. Sin embargo, la bendición es de hecho una propiedad de la variable, no de su valor, por lo que, por ejemplo, asignar la variable bendecida real a otra simplemente le proporciona una copia superficial y sin bendiciones. Ver perlobj para más detalles.fuente
Además de lo que Eric ha dicho, considere el siguiente código C:
A diferencia de los lenguajes como Python, C #, Java o lo que sea, lo anterior está mal escrito porque perdemos información de tipo. Eric señaló correctamente que en C # podemos eludir el compilador lanzando, efectivamente diciéndole "Sé más sobre el tipo de esta variable que tú".
¡Pero incluso entonces, el tiempo de ejecución seguirá comprobando el tipo! Si el lanzamiento no es válido, el sistema de tiempo de ejecución lo detectará y generará una excepción.
Con la eliminación de tipos, esto no sucede: la información de tipos se desecha. Un yeso
void*
en C hace exactamente eso. En este sentido, lo anterior es fundamentalmente diferente de una declaración de método C # comovoid f(Object x)
.(Técnicamente, C # también permite el borrado de tipo a través de código inseguro o clasificación).
Esto es tan débilmente escrito como se pone. Todo lo demás es solo una cuestión de verificación de tipo estático versus dinámico, es decir, del momento en que se verifica un tipo.
fuente
void*
rompe a través de ambos controles de tipo. El borrado de tipo genérico no lo hace, solo evita las verificaciones en tiempo de compilación. Es exactamente como los moldes explícitos (mencionados por Eric) a este respecto.Un ejemplo perfecto proviene del artículo de Wikipedia de Strong Typing :
En general, la tipificación fuerte implica que el lenguaje de programación impone restricciones severas en la mezcla que se permite que ocurra.
Mecanografía débil
Typing fuerte
Tenga en cuenta que un lenguaje de escritura débil puede mezclar diferentes tipos sin errores. Un lenguaje de tipo fuerte requiere que los tipos de entrada sean los tipos esperados. En un lenguaje de tipo fuerte, un tipo se puede convertir (
str(a)
convierte un entero en una cadena) o emitir (int(b)
).Todo esto depende de la interpretación de la escritura.
fuente
Me gustaría contribuir a la discusión con mi propia investigación sobre el tema, ya que otros comentan y contribuyen. He estado leyendo sus respuestas y siguiendo sus referencias y he encontrado información interesante. Como se sugirió, es probable que la mayoría de esto se discuta mejor en el foro de Programadores, ya que parece ser más teórico que práctico.
Desde un punto de vista teórico, creo que el artículo de Luca Cardelli y Peter Wegner titulado Sobre comprensión de tipos, abstracción de datos y polimorfismo tiene uno de los mejores argumentos que he leído.
Esta afirmación parece sugerir que escribir débilmente nos permitiría acceder a la estructura interna de un tipo y manipularlo como si fuera otra cosa (otro tipo). Quizás lo que podríamos hacer con un código inseguro (mencionado por Eric) o con punteros borrados de tipo c mencionados por Konrad.
El artículo continúa ...
Como tal, la escritura fuerte significa la ausencia de errores de escritura, solo puedo suponer que la escritura débil significa lo contrario: la probable presencia de errores de escritura. ¿En tiempo de ejecución o tiempo de compilación? Parece irrelevante aquí.
Lo gracioso, según esta definición, un lenguaje con poderosas coacciones de tipo como Perl se consideraría fuertemente tipado, porque el sistema no falla, pero se trata de los tipos coercitándolos en equivalencias apropiadas y bien definidas.
Por otro lado, podría decir que la asignación de
ClassCastException
yArrayStoreException
(en Java) yInvalidCastException
,ArrayTypeMismatchException
(en C #) que indicaría un nivel de mecanografía débilmente, al menos en tiempo de compilación? La respuesta de Eric parece estar de acuerdo con esto.En un segundo artículo llamado Programful Typeful Programming proporcionado en una de las referencias proporcionadas en una de las respuestas en esta pregunta, Luca Cardelli profundiza en el concepto de violaciones de tipo:
Como tal, las coacciones de tipo como las proporcionadas por los operadores podrían considerarse violaciones de tipo, pero a menos que rompan la consistencia del sistema de tipo, podríamos decir que no conducen a un sistema de tipo débil.
En base a esto, ni Python, Perl, Java o C # están mal escritos.
Cardelli menciona dos tipos de opiniones que considero muy bien casos de mecanografía realmente débil:
Este tipo de cosas posibles en lenguajes como C (mencionado por Konrad) o mediante código inseguro en .Net (mencionado por Eric) realmente implicaría escribir débilmente.
Creo que la mejor respuesta hasta ahora es la de Eric, porque la definición de estos conceptos es muy teórica, y cuando se trata de un lenguaje en particular, las interpretaciones de todos estos conceptos pueden llevar a diferentes conclusiones discutibles.
fuente
Una escritura débil significa que un alto porcentaje de tipos puede ser coaccionado implícitamente, intentando adivinar qué pretendía el codificador.
La tipificación fuerte significa que los tipos no están obligados, o al menos están menos obligados.
La tipificación estática significa que los tipos de sus variables se determinan en tiempo de compilación.
Muchas personas recientemente han estado confundiendo "mecanografiado manifiestamente" con "mecanografiado fuertemente". "Tipo de manifiesto" significa que declara los tipos de sus variables explícitamente.
Python está en su mayoría fuertemente tipado, aunque puede usar casi cualquier cosa en un contexto booleano, y los booleanos se pueden usar en un contexto entero, y puede usar un entero en un contexto flotante. No se escribe de manera manifiesta, porque no necesita declarar sus tipos (excepto Cython, que no es completamente python, aunque interesante). Tampoco está tipificado estáticamente.
C y C ++ están expresamente escritos, estáticamente escritos y algo fuertemente tipados, porque declaras tus tipos, los tipos se determinan en tiempo de compilación, y puedes mezclar enteros y punteros, o enteros y dobles, o incluso lanzar un puntero a un tipo en un puntero a otro tipo.
Haskell es un ejemplo interesante, porque no está escrito de forma manifiesta, sino que también está escrito de forma estática y fuerte.
fuente
La tipificación fuerte <=> débil no se trata solo del continuo sobre cuánto o cuán poco de los valores son coaccionados automáticamente por el idioma para un tipo de datos a otro, sino cuán fuerte o débilmente se escriben los valores reales . En Python y Java, y principalmente en C #, los valores tienen sus tipos establecidos en piedra. En Perl, no tanto: en realidad solo hay un puñado de diferentes tipos de valores para almacenar en una variable.
Abramos los casos uno por uno.
Pitón
En el ejemplo de Python
1 + "1"
, el+
operador llama al__add__
tipo paraint
darle la cadena"1"
como argumento; sin embargo, esto da como resultado NotImplemented:A continuación, el intérprete prueba el
__radd__
de str:Como falla, el
+
operador falla con el resultadoTypeError: unsupported operand type(s) for +: 'int' and 'str'
. Como tal, la excepción no dice mucho sobre la escritura fuerte, pero el hecho de que el operador+
no coaccione sus argumentos automáticamente al mismo tipo, es un indicador del hecho de que Python no es el lenguaje más débilmente escrito en el continuo.Por otro lado, en Python
'a' * 5
se implementa:Es decir,
El hecho de que la operación sea diferente requiere un tipo de escritura fuerte; sin embargo, lo contrario de
*
coaccionar los valores a números antes de multiplicar aún no necesariamente haría que los valores se tipeen débilmente.Java
El ejemplo de Java
String result = "1" + 1;
funciona solo porque, por conveniencia, el operador+
está sobrecargado de cadenas. El+
operador Java reemplaza la secuencia con la creación deStringBuilder
(ver esto ):Este es más bien un ejemplo de tipeo muy estático, sin ninguna coerción real:
StringBuilder
tiene un métodoappend(Object)
que se usa específicamente aquí. La documentación dice lo siguiente:Donde
String.valueOf
entoncesPor lo tanto, este es un caso de absolutamente ninguna coerción por parte del lenguaje, delegando toda preocupación a los objetos en sí.
C#
De acuerdo con la respuesta de Jon Skeet aquí , el operador
+
ni siquiera está sobrecargado para lastring
clase, similar a Java, esto es solo la conveniencia generada por el compilador, gracias a la escritura tanto estática como fuerte.Perl
Como explica el perldata ,
Sin embargo, Perl no tiene un tipo de datos separado para números, booleanos, cadenas, nulos,
undefined
s, referencias a otros objetos, etc., solo tiene un tipo para todos estos, el tipo escalar; 0 es un valor escalar tanto como "0". Una variable escalar que se estableció como una cadena realmente puede cambiar a un número y, a partir de ahí, comportarse de manera diferente a "solo una cadena", si se accede a ella en un contexto numérico. El escalar puede contener cualquier cosa en Perl, es tanto el objeto como existe en el sistema. mientras que en Python los nombres solo se refieren a los objetos, en Perl los valores escalares en los nombres son objetos modificables. Además, el sistema de tipo orientado a objetos está pegado encima de esto: solo hay 3 tipos de datos en perl: escalares, listas y hashes. Un objeto definido por el usuario en Perl es una referencia (es decir, un puntero a cualquiera de los 3 anteriores)bless
editado a un paquete: puede tomar dicho valor y bendecirlo a cualquier clase en el instante que desee.Perl incluso le permite cambiar las clases de valores a su antojo; esto no es posible en Python, donde crear un valor de alguna clase necesita construir explícitamente el valor que pertenece a esa clase con
object.__new__
o similar. En Python realmente no puedes cambiar la esencia del objeto después de la creación, en Perl puedes hacer casi cualquier cosa:por lo tanto, la identidad de tipo está débilmente ligada a la variable y se puede cambiar a través de cualquier referencia sobre la marcha. De hecho, si lo haces
\$another
no tiene la identidad de clase, aunque\$val
todavía dará la referencia bendecida.TL; DR
Hay mucho más sobre la escritura débil en Perl que solo las coerciones automáticas, y se trata más de que los tipos de los valores en sí mismos no están establecidos en piedra, a diferencia de Python, que es un lenguaje de escritura dinámica pero muy fuerte. Eso da pitón
TypeError
en1 + "1"
una indicación de que el lenguaje es fuertemente tipado, aunque el uno en contra de hacer algo útil, como en Java o C # no se opone a ellos siendo idiomas inflexible.fuente
Como muchos otros han expresado, la noción completa de mecanografía "fuerte" versus "débil" es problemática.
Como arquetipo, Smalltalk está muy fuertemente tipado: siempre generará una excepción si una operación entre dos objetos es incompatible. Sin embargo, sospecho que pocos en esta lista llamarían a Smalltalk un lenguaje fuertemente tipado, porque se escribe dinámicamente.
Encuentro la noción de mecanografía "estática" versus "dinámica" más útil que "fuerte" versus "débil". Un lenguaje de tipo estático tiene todos los tipos resueltos en tiempo de compilación, y el programador tiene que declarar explícitamente si no.
Contraste con un lenguaje de escritura dinámica, donde la escritura se realiza en tiempo de ejecución. Por lo general, esto es un requisito para los lenguajes polimórficos, de modo que las decisiones sobre si una operación entre dos objetos es legal no tiene que ser decidida previamente por el programador.
En lenguajes polimórficos, de tipo dinámico (como Smalltalk y Ruby), es más útil pensar en un "tipo" como una "conformidad con el protocolo". Si un objeto obedece un protocolo de la misma manera que otro objeto, incluso si los dos objetos no comparten ninguna herencia o mixins u otro vudú, el sistema de tiempo de ejecución los considera el mismo "tipo". Más correctamente, un objeto en tales sistemas es autónomo y puede decidir si tiene sentido responder a un mensaje en particular que se refiera a un argumento en particular.
¿Quiere un objeto que pueda dar una respuesta significativa al mensaje "+" con un argumento de objeto que describa el color azul? Puede hacerlo en lenguajes de tipo dinámico, pero es una molestia en los idiomas de tipo estático.
fuente
Me gusta la respuesta de @Eric Lippert , pero para abordar la pregunta, los lenguajes fuertemente tipados generalmente tienen un conocimiento explícito de los tipos de variables en cada punto del programa. Los idiomas con tipos débiles no lo hacen, por lo que pueden intentar realizar una operación que puede no ser posible para un tipo en particular. Creo que la forma más fácil de ver esto es en una función. C ++:
Se
a
sabe que la variable es de tipo cadena y cualquier operación incompatible se detectará en tiempo de compilación.Pitón:
La variable
a
podría ser cualquier cosa y podemos tener un código que llame a un método no válido, que solo quedará atrapado en tiempo de ejecución.fuente