¿Por qué no puede haber conversiones implícitas?

13

Según tengo entendido, las conversiones implícitas pueden causar errores.

Pero eso no tiene sentido, ¿no deberían las conversiones normales también causar errores, entonces?

¿Por qué no tener

len(100)

trabaje por el lenguaje interpretándolo (o compilándolo) como

len(str(100))

especialmente porque esa es la única manera (que conozco) para que funcione. El idioma sabe cuál es el error, ¿por qué no lo arreglamos?

Para este ejemplo utilicé Python, aunque creo que para algo tan pequeño es básicamente universal.

    
pregunta Quelklef 26.05.2015 - 03:20

9 respuestas

36

Para lo que vale, len(str(100)) , len(chr(100)) y len(hex(100)) son diferentes. str es no la única forma de hacerlo funcionar, ya que hay más de una conversión diferente en Python de un entero a una cadena. Uno de ellos, por supuesto, es el más común, pero no es necesario decir que ese es el que quisiste decir. Una conversión implícita significa literalmente, "no hace falta decirlo".

Uno de los problemas prácticos con las conversiones implícitas es que no siempre es obvio qué conversión se aplicará cuando haya varias posibilidades, y esto hace que los lectores cometan errores al interpretar el código porque fallan Para averiguar la conversión implícita correcta. Todos siempre dicen que lo que pretendían era la "interpretación obvia". Es obvio para ellos porque es lo que querían decir. Puede que no sea obvio para otra persona.

Es por esto que (la mayoría de las veces) Python prefiere lo explícito a lo implícito, prefiere no arriesgarlo. El caso principal en el que Python sí escribe la coacción es en aritmética. Permite 1 + 1.0 porque la alternativa sería demasiado molesta para vivir, pero no permite 1 + "1" porque cree que deberías especificar si te refieres a int("1") , float("1") , ord("1") , str(1) + "1" , o algo mas. Tampoco permite que (1,2,3) + [4,5,6] , aunque podría definir reglas para elegir un tipo de resultado, al igual que define reglas para elegir el tipo de resultado de 1 + 1.0 .

Otros idiomas no están de acuerdo y tienen muchas conversiones implícitas. Cuanto más incluyen, menos obvios se vuelven. ¡Intenta memorizar las reglas del estándar C para "promociones enteras" y "conversiones aritméticas habituales" antes del desayuno!

    
respondido por el Steve Jessop 26.05.2015 - 14:32
27
  

Según tengo entendido, las conversiones implícitas pueden causar errores.

Te estás perdiendo una palabra: las conversiones implícitas pueden causar errores en tiempo de ejecución .

Para un caso simple como el que se muestra, es bastante claro lo que quería decir. Pero los idiomas no pueden funcionar en los casos. Necesitan trabajar con reglas. Para muchas otras situaciones, no está claro si el programador cometió un error (usando el tipo incorrecto) o si el programador tuvo la intención de hacer lo que el código supone que pretendía hacer.

Si el código se supone incorrecto, aparece un error de tiempo de ejecución. Ya que son tediosos de rastrear, muchos idiomas se equivocan al decirle que cometió un error y le permite decirle a la computadora lo que realmente quería decir (corregir el error o hacer la conversión explícitamente). Otros idiomas hacen la conjetura, ya que su estilo se presta a un código rápido y fácil que es más fácil de depurar.

Una cosa a tener en cuenta es que las conversiones implícitas hacen que el compilador sea un poco más complejo. Debe ser más cuidadoso con los ciclos (¡intentemos esta conversión de A a B; oops que no funcionó, pero hay una conversión de B a A! y luego también debe preocuparse por los ciclos de tamaño C y D ), que es una pequeña motivación para evitar conversiones implícitas.

    
respondido por el Telastyn 26.05.2015 - 03:33
14

Las conversiones implícitas son bastante posibles de hacer. La situación en la que te metes en problemas es cuando no sabes cómo debería funcionar algo.

Un ejemplo de esto se puede ver en Javascript, donde el operador + funciona de diferentes maneras en diferentes momentos.

>>> 4 + 3
7
>>> "4" + 3
43
>>> 4 + "3"
43

Si uno de los argumentos es una cadena, entonces el operador + es una concatenación de cadenas, de lo contrario es una adición.

Si te dan un argumento y no sabes si es una cadena o un entero, y quieres hacer una adición, puede ser un poco complicado.

Otra forma de lidiar con esto es desde el patrimonio Básico (ese perl sigue desde - vea La programación es difícil, vamos a hacer scripts ... )

En Básico, la función len solo tiene sentido invocarse en una Cadena (docs para visual basic :" Cualquier expresión de String válida o nombre de variable. Si Expression es de tipo Object, la función Len devuelve el tamaño tal como se escribirá en el archivo con la función FilePut. ").

Perl sigue este concepto de contexto. La confusión que existe en JavaScript con la conversión implícita de tipos para el operador + es a veces una adición y otras veces la concatenación no ocurre en perl porque + es siempre además y . es siempre concatenación.

Si se usa algo en un contexto escalar, es un escalar (por ejemplo, al usar una lista como escalar, la lista se comporta como si fuera un número correspondiente a su longitud). Si usa un operador de cadena ( eq para la prueba de igualdad, cmp para la comparación de cadena), el escalar se usa como si fuera una cadena. Del mismo modo, si se usó algo en un contexto matemático ( == para la prueba de igualdad y <=> para la comparación numérica), el escalar se usa como si fuera un número.

La regla fundamental para toda la programación es "hacer lo que menos sorprenda a la persona". Esto no significa que no haya sorpresas allí, pero el esfuerzo es sorprender lo más mínimo a la persona.

Dirigiéndose a un primo cercano de perl - php, hay situaciones en las que un operador puede actuar sobre algo en contextos numéricos o de cadena y el comportamiento puede ser sorprendente para las personas. El operador ++ es uno de estos ejemplos. En los números, se comporta exactamente como se esperaba. Cuando actúa sobre una cadena, como "aa" , incrementa la cadena ( $foo = "aa"; $foo++; echo $foo; imprime ab ). También se reiniciará para que az cuando se incremente se convierta en ba . Esto no es particularmente sorprendente todavía.

$foo = "3d8";
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";

( ideone )

Esto se imprime:

3d8
3d9
3e0
4

Bienvenido a los peligros de las conversiones implícitas y los operadores que actúan de manera diferente en la misma cadena. (Perl maneja ese bloque de código de manera un poco diferente: decide que "3d8" cuando se aplica el operador ++ es un valor numérico desde el inicio y va a 4 de inmediato ( ideone ) - este comportamiento está bien descrito en perlop : Incremento automático y decremento automático )

Ahora, ¿por qué un lenguaje hace algo de una manera y otro lo hace de otra manera para llegar a los pensamientos de diseño de los diseñadores? La filosofía de Perl es Hay más de una forma de hacerlo , y puedo pensar en varias formas de hacerlo. de estas operaciones. Por otro lado, Python tiene una filosofía descrita en PEP 20 - El zen de Python que declara (entre otras cosas): "Debería haber una, y preferiblemente solo una forma obvia de hacerlo".

Estas diferencias de diseño han conducido a diferentes idiomas. Hay una forma de obtener la longitud de un número en Python. La conversión implícita va en contra de esta filosofía.

Lectura relacionada: ¿Por qué Ruby no tiene la conversión implícita de Fixnum en String?

    
respondido por el user40980 26.05.2015 - 04:46
11

ColdFusion hizo la mayor parte de esto. Define un conjunto de reglas para manejar sus conversiones implícitas, tiene un solo tipo de variable y listo.

El resultado es una anarquía total, donde agregar "4a" a 6 es 6.16667.

¿Por qué? Bien, porque la primera de las dos variables es un número, entonces el resultado será numérico. "4a" se analiza como una fecha y se ve como "4 AM". 4 AM es 4: 00/24: 00, o 1/6 de un día (0.16667). Suma a 6 y obtienes 6.16667.

Las listas tienen caracteres separadores predeterminados de una coma, por lo que si alguna vez agrega un elemento a una lista que contiene una coma, entonces acaba de agregar dos elementos. Además, las listas son cadenas secretas, por lo que pueden analizarse como fechas si solo contienen 1 elemento.

La comparación de cadenas comprueba si ambas cadenas pueden analizarse primero en las fechas. Porque no hay un tipo de fecha, lo hace. Lo mismo para números y cadenas que contienen números, notación octal y valores booleanos ("true" y "yes", etc.)

En lugar de fallar rápidamente y reportar un error, en lugar de eso ColdFusion manejará las conversiones de tipos de datos por usted. Y no de una buena manera.

Por supuesto, puedes arreglar las conversiones implícitas llamando a funciones explícitas como DateCompare ... pero luego pierdes los "beneficios" de la conversión implícita.

Para ColdFusion, esta es una manera de ayudar a empoderar a los desarrolladores. Especialmente cuando todos los desarrolladores están acostumbrados a HTML (ColdFusion funciona con etiquetas, se llama CFML, más tarde agregaron scripts a través de <cfscript> también, donde no es necesario agregar etiquetas para todo). Y funciona bien cuando solo quieres que algo funcione. Pero cuando necesita que las cosas sucedan de una manera más precisa, necesita un lenguaje que se niegue a hacer conversiones implícitas para cualquier cosa que parezca que podría haber sido un error.

    
respondido por el Pimgd 26.05.2015 - 09:57
7

Está diciendo que las conversiones implícitas podrían ser una buena idea para operaciones que no son ambiguas, como int a = 100; len(a) , donde obviamente quiere convertir el int en una cadena antes de llamar.

Pero estás olvidando que estas llamadas pueden ser sintácticamente sin ambigüedades, pero pueden representar un error tipográfico realizado por el programador que pretendía pasar a1 , que es una cuerda. Este es un ejemplo artificial, pero con los IDE que proporcionan autocompletado para nombres de variables, estos errores ocurren.

El sistema de tipos apunta a ayudarnos a evitar errores, y para los idiomas que eligen una verificación de tipo más estricta, las conversiones implícitas lo socavan.

Verifique los problemas de Javascript con todas las conversiones implícitas realizadas por ==, tanto que muchos ahora recomiendan quedarse con el operador de conversión no implícita ===.

    
respondido por el Avner Shahar-Kashtan 26.05.2015 - 07:59
6

Imagina por un momento el contexto de tu declaración. Usted dice que esta es la "única manera" en que podría funcionar, pero ¿está realmente seguro de que funcionará así? ¿Qué hay de esto:

def approx_log_10(s):
    return len(s)
print approx_log_10(3.5)  # "3" is probably not what I'm expecting here...

Como han mencionado otros, en su ejemplo específico, parece simple para el compilador intuir lo que quería decir. Pero en un contexto más amplio de programas más complicados, a menudo no es del todo obvio si pretendía ingresar una cadena, o tipear un nombre de variable para otra, o llamar a la función incorrecta, o cualquiera de las docenas de otros posibles errores lógicos. .

En el caso en el que el intérprete / compilador adivina lo que quisiste decir, a veces funciona mágicamente, y luego otras veces pasas horas depurando algo que misteriosamente no funciona sin ninguna razón aparente. Es mucho más seguro, en el caso promedio, que se le diga que la afirmación no tiene sentido y que dedique unos segundos a corregir lo que realmente quiso decir.

    
respondido por el GrandOpener 26.05.2015 - 08:47
3

Las conversiones implícitas pueden ser un verdadero dolor al trabajar con ellas. En PowerShell:

$a = $(dir *.xml) # typeof a is a list, because there are two XML files in the folder.
$a = $(dir *.xml) # typeof a is a string, because there is one XML file in the folder.

De repente, se necesitan el doble de pruebas y el doble de errores que habría sin una conversión implícita.

    
respondido por el Esben Skov Pedersen 26.05.2015 - 10:34
2

Los lanzamientos explícitos son importantes porque dejan clara su intención. En primer lugar, el uso de conversos explícitos le cuenta una historia a alguien que está leyendo su código. Revelan que intencionalmente hiciste lo que hiciste. Además lo mismo se aplica al compilador. Lo siguiente es ilegal en C # por ejemplo

double d = 3.1415926;
// code elided
int i = d;

El yeso hará que pierdas precisión, lo que fácilmente podría ser un error. Por lo tanto el compilador se niega a compilar. Al usar un reparto explícito, le está diciendo al compilador: "Oye, sé lo que estoy haciendo". Y él irá: "¡De acuerdo!" y compilará.

    
respondido por el Paul Kertscher 26.05.2015 - 11:50
1

Los idiomas que admiten conversiones / conversiones implícitas, o la escritura débil como a veces se denomina, harán suposiciones para usted que no siempre coinciden con el comportamiento que deseaba o esperaba. Es posible que los diseñadores del lenguaje hayan tenido el mismo proceso de pensamiento que tuvo para un cierto tipo de conversión o serie de conversiones, y en ese caso nunca verá un problema; Sin embargo, si no lo hicieron, entonces su programa fallará.

El fallo, sin embargo, puede o no ser obvio. Dado que estos lanzamientos implícitos ocurren en tiempo de ejecución, su programa se ejecutará felizmente y solo verá un problema cuando mire la salida o el resultado de su programa. Un lenguaje que requiere conversiones explícitas (algunos se refieren a esto como una tipificación fuerte) le daría un error antes de que el programa comience a ejecutarse, lo cual es un problema mucho más obvio y más fácil de solucionar para que las conversiones implícitas salgan mal.

El otro día, un amigo mío le preguntó a su hijo de 2 años qué eran 20 + 20 y él respondió 2020. Le dije a mi amigo: "Se convertirá en un programador de Javascript".

En Javascript:

20+20
40

20+"20"
"2020"

Por lo tanto, puede ver los problemas que pueden generar las conversiones implícitas y por qué no es algo que solo pueda solucionarse. La solución que argumentaría, es utilizar conversiones explícitas.

    
respondido por el Fred Thomsen 29.05.2015 - 05:46

Lea otras preguntas en las etiquetas