¿Por qué las variables necesitan un tipo?

10

Entonces escribimos:

Customer c = new Customer();

¿Por qué el diseño no es tal que escribimos?

c = new Customer();
c.CreditLimit = 1000;

El compilador puede calcular puntos c para un Cliente y permitir que los miembros del Cliente sean invocados en c?

Sé que podemos querer escribir:

IPerson c = new Customer();
IPerson e = new Employee();

para poder escribir:

public string GetName(IPerson object)
{
    return object.Name
}

string name = GetName(c); // or GetName(e);

Pero si escribimos:

c = new Customer();
e = new Employee();

aún podríamos escribir:

public string GetName(object)
{
    return object.Name
}    

string name = GetName(c); // or GetName(e);

El compilador podría quejarse del código inmediatamente anterior si el tipo de objeto c referencias no admite una propiedad Nombre (ya que puede verificar qué miembros se usan en el argumento / parámetro dentro del método), o el tiempo de ejecución podría quejarse.

Incluso con la palabra clave dinámica de C #, todavía estamos usando una variable 'tipo' (determinada en tiempo de ejecución). Pero, ¿por qué una variable necesita un tipo? Estoy seguro de que debe haber una buena razón, ¡pero no puedo pensar en eso!

sturdytree
fuente
12
Es por eso que hay lenguajes dinámicos como Python y Ruby (y otros). No hay respuesta a esta pregunta. Es un hecho que algunos idiomas usan declaraciones de tipo y otros no.
S.Lott
2
"¿Las variables en esos idiomas no tienen ningún tipo?" Correcto. Las variables no tienen un tipo en Python. Los objetos tienen un tipo y las variables son simplemente referencias a objetos. Tu pregunta es realmente solo una observación. Específicamente "no todos los idiomas requieren declaraciones variables". No hay respuesta. Por lo tanto, es probable que se cierre como no constructivo.
S.Lott
1
Es posible que desee echar un vistazo a Boo , que le permite declarar variables sin ninguna declaración de tipos, pero utiliza la inferencia de tipos para descubrir los tipos para que no sacrifique la corrección y los beneficios de rendimiento de una escritura fuerte.
Mason Wheeler
2
var x = 1; - ¿Te refieres a 32 bits, 64 bits, número de 16 bits? ¿byte? ¿flotador? ¿doble? ¿decimal? Esto solo se trata de primitivas.
Trabajo
44
Puede usar varen C # y ser explícito acerca de dónde desea declarar una variable siempre es bueno.
Daniel Little

Respuestas:

22

Pero, ¿por qué una variable necesita un tipo?

  1. Esto puede detectar errores en los que se asigna una expresión inválida, mal escrita a una variable. Algunos idiomas tienen escritura dinámica , lo que sacrifica las garantías de corrección de un tipo por variable por el tipo de flexibilidad que parece desear.
  2. Los tipos pueden permitir que el compilador genere código más eficiente. La escritura dinámica significa que las verificaciones de tipo deben realizarse en tiempo de ejecución.
Fred Foo
fuente
1
Por favor explique el voto negativo.
Fred Foo
44
Además, la escritura dinámica tiene un costo de rendimiento, ya que la información de tipo debe verificarse durante el tiempo de ejecución.
Charles Salvia
@CharlesSalvia: buen punto, agregó eso a la respuesta.
Fred Foo
2
@sturdytree: en cuanto a "si no tiene un tipo, entonces no se puede escribir incorrectamente", en lo que respecta a las reglas del idioma, eso es cierto. Pero a la variable aún se le puede asignar el tipo incorrecto desde un punto de vista semántico , por ejemplo, escribió mal el nombre de un constructor y el programa aún se ejecuta, pero no hace lo que desea.
Fred Foo
55
@sturdytree Si bien es cierto que no hay lenguas que tienen verdaderamente sin tipos variables con la comprobación de tipos, hay lenguas que tienen inferencia de tipos . Es decir, el lenguaje analiza su uso de la variable y deduce el tipo de su uso. Si hay un conflicto (por ejemplo, realiza a = new Bar()y luego llama a un método de la clase Baz), el compilador genera un error. Lenguajes como Haskell y OCaml fueron pioneros en la inferencia de tipos, pero está presente en C #, con la varpalabra clave.
Quanticle
9

Tiene un punto perfectamente válido, existen idiomas que no realizan un seguimiento del tipo de una variable y se denominan "de tipo dinámico". La categoría incluye lenguajes como JavaScript, Perl, Lisp y Python.

La ventaja que obtenemos de un lenguaje de tipo estático es una comprobación adicional de errores en tiempo de compilación.

Supongamos, por ejemplo, que tiene el siguiente método:

public addCustomerContact(Customer client, Employee contact) {
   ...
} 

Sería posible, si tiene un cliente boby un empleado jamesen su código, llamar por error addCustomerContact(james, bob), lo que no es válido. Pero si el compilador no conoce los tipos de variables, no puede advertirle que ha realizado una llamada no válida, en su lugar, se produce un error en tiempo de ejecución ... y dado que los lenguajes de tipo dinámico no comprueban tipo de parámetros pasados ​​a los métodos, ese problema ocurre cada vez que su código intenta usar propiedades del jamesobjeto solo para clientes o propiedades del objeto solo para empleados bob. Eso puede ser mucho después de que el par (james, bob) se haya agregado a la lista de contactos de clientes.

Ahora, se preguntarán, ¿por qué el compilador aún no puede inferir el tipo de jamesy bob, y todavía nos advierte? Eso a veces puede ser posible, pero si las variables realmente no tienen tipo, entonces podríamos hacer lo siguiente:

var james;
var bob;
if (getRandomNumber() > 0.5) {
   james = new Customer();
   bob = new Employee();
} else {
   james = new Employee();
   bob = new Customer();
}

Es perfectamente legal asignar cualquier valor a cualquier variable, ya que dijimos que las variables no tienen tipo. Eso también significa que no siempre podemos conocer el tipo de una variable, porque podría ser de diferentes tipos en función de diferentes rutas de ejecución.

En general, los lenguajes de tipo dinámico se utilizan para los lenguajes de secuencias de comandos, donde no hay un paso de compilación, por lo que no existen errores de compilación, lo que significa que las pulsaciones de teclas adicionales necesarias para dar el tipo de variables no serían muy útiles.

También hay algunas ventajas distintas para los lenguajes de tipo dinámico, principalmente en términos de que se necesita menos código para implementar el mismo diseño: las interfaces no necesitan estar escritas, porque todo está "tipado en pato" (solo nos importa qué métodos / propiedades tiene un objeto) , no a qué clase pertenece el objeto), no es necesario que las variables tengan un tipo explícito ... con el compromiso de que descubrimos unos pocos errores antes de comenzar a ejecutar nuestro código.

Theodore Murdock
fuente
Gracias Theodore "Pero si el compilador no conoce los tipos de variables, no puede advertirle que ha realizado una llamada no válida" Como usted menciona, el compilador puede conocer los tipos de objetos a los que apuntan las variables.
sturdytree
Theodore: En su ejemplo james / bob, como programadores deberíamos saber cómo hemos utilizado nuestras variables (y una buena ayuda para nombrar) y, por lo tanto, no veo ningún problema con ellas. Cuando dice "no siempre podemos saber el tipo de variable", supongo que quiere decir que no siempre podemos saber el tipo de objeto al que apunta la variable, pero el compilador puede resolver esto y advertir sobre miembros incorrectos aplicado (es decir, podemos tener una comprobación estática).
sturdytree
2
He dado un ejemplo anterior en el que no puedes conocer los tipos de objetos estáticamente ... dependiendo de la ruta de ejecución, el tipo de objeto almacenado en una variable sin información de tipo puede ser diferente. Puede tener un lenguaje como C # donde se puede inferir la información del tipo de tiempo de compilación, pero, que yo sepa, no hay lenguaje con verificación de tipo estático y variables verdaderamente sin tipo, la complejidad computacional del análisis estático también es probable Excelente.
Theodore Murdock
Theodore, gracias, has entendido correctamente que de lo que estoy hablando es de un lenguaje con verificación de tipo estático (basado en el tipo de objeto) y variables sin tipo. Es triste escuchar que no hay ninguno: me dijeron que Python tiene variables sin tipo, pero parece que no tiene una comprobación de tipo estático.
sturdytree
1
-1; la escritura fuerte / débil es ortogonal a la escritura estática / dinámica. C está estáticamente débilmente tipado; Lisp y Python están dinámicamente fuertemente tipados.
Fred Foo
5

Por lo tanto, los programadores profesionales no tienen que determinar si

10 + "10"

is "1010" or 20....

Lo que es un error, en tiempo de compilación con un lenguaje tipado estáticamente o en tiempo de ejecución con uno tipado dinámicamente. Bien cuerdos de todos modos.

Tony Hopkinson
fuente
2
Es decir, ¿Perl no es un idioma cuerdo? :)
Fred Foo
55
@larsmans: de hecho, no, no lo es. Pero eso es puramente opinión.
NotMe
2
@ChrisLively: Me alegra que sea una cuestión de hecho ahora. Visite mi lugar de trabajo en cualquier momento para convencer a mis colegas amantes de Perl;)
Fred Foo
2
10 + "10" está utilizando un operador '+' en objeto de tipo entero y objeto de tipo cadena. El compilador emitiría un error. Mi pregunta tiene que ver con el tipo de variable, no con el objeto.
sturdytree
2
Que es C válida: es puntero de la aritmética.
dan04
4

Suponiendo que tenía una variable one(establecida en 1) e intentó evaluar one + one. Si no tenía idea del tipo, entonces 1 + 1 será ambiguo. Puede argumentar que 2 u 11 podrían ser respuestas correctas. Se vuelve ambiguo si no se da contexto.

He visto que esto sucede en SQLite, donde los tipos de bases de datos se configuraron inadvertidamente en VARCHARlugar de INTy cuando se realizaron operaciones, las personas obtenían resultados inesperados.

En c # si el contexto infiere un tipo, puede usar la varpalabra clave.

var c = new Customer();
var e = new Employer();

Compilará c y e con los tipos inferidos en tiempo de compilación.

Stephen Quan
fuente
1
En Python, 1 + 1siempre tiene el tipo int, pero no hay necesidad de declararlo. La pregunta es por qué las variables tienen un tipo, no valores .
Fred Foo
Lo sentimos que estaba destinado a ver variablesno valuescuando utilicé 1 + 1en mi ejemplo. Supongo que no estaba claro.
Stephen Quan
Aún así, los idiomas escritos dinámicamente pueden lidiar con esto. En Python, one=1; print(one+one)impresiones 2. one="1"; print(one+one)impresiones 11. El ejemplo de SQLite es más convincente, pero el problema es uno de tipeo débil, por lo que no es realmente relevante para C #.
Fred Foo
En mi esquema sugerido, uno = 1 significa una variable que apunta a un tipo entero (no estoy sugiriendo ningún tipo, los objetos tendrían un tipo). uno + uno no sería ambiguo (estamos agregando dos objetos / valores enteros)
sturdytree
1
Hola @ dan04, lo vi sin ORDER BYdarse cuenta en un VARCHARcampo. Ver stackoverflow.com/questions/9103313/… .
Stephen Quan
4

Una variable no necesita tener un tipo asociado. Los idiomas en los que esto es cierto incluyen Lisp, Scheme, Erlang, Prolog, Smalltalk, Perl, Python, Ruby y otros.

También es posible que una variable tenga un tipo, pero es posible que no tenga que escribir el tipo en el programa. Esto generalmente se llama inferencia de tipos. ML, Haskell y sus descendientes tienen una poderosa inferencia de tipos; algunos otros lenguajes lo tienen en formas menores, como las autodeclaraciones de C ++ .

El argumento principal contra la inferencia de tipos es que daña la legibilidad. Por lo general, es más fácil entender el código cuando se escriben los tipos.

Ryan Culpepper
fuente
1

Cuando identifica el tipo que representa su variable, está haciendo una declaración sobre un par de cosas. Está identificando los requisitos de asignación de memoria para su variable y está definiendo las reglas de compatibilidad y rango para su variable. Proporciona una manera de evitar confusiones sobre sus intenciones para los datos que está almacenando, y para proporcionarle un medio relativamente barato para identificar posibles problemas en su código en el momento de la compilación.

Si declara las siguientes variables:

myVar      = 5;
myOtherVar = "C";

¿Qué se puede inferir acerca de estas variables? ¿Está myVarfirmado o sin firmar? ¿Es de 8 bits, 64 bits o algo intermedio? ¿Es myOtherVaruna cadena (efectivamente una matriz) o un Char? ¿Es ANSI o Unicode?

Al proporcionar tipos de datos específicos, proporciona al compilador pistas sobre cómo puede optimizar los requisitos de memoria para su aplicación. Algunos idiomas no se molestan demasiado con este tipo de cosas, lo que permite que esos asuntos se traten en tiempo de ejecución, mientras que otros idiomas permitirán una cierta cantidad de tipeo dinámico porque al analizar el código se pueden inferir los tipos de datos.

Otro punto con los idiomas fuertemente tipados es que le ahorra la necesidad de proporcionar instrucciones al compilador cada vez que usa una variable. ¡¿Te imaginas lo horrible e ilegible que se volvería tu código si cada vez que accedieras a una variable, tuvieras que lanzarlo de manera efectiva para decirle al compilador qué tipo de valor era? !!

S.Robins
fuente
Buen punto, aunque el compilador podría usar el tipo más eficiente (por ejemplo, int pequeño) basado en el valor, puedo ver que podemos querer que "C" sea un objeto de cadena en lugar de char para poder realizar ciertas operaciones . Sin embargo, en tales casos, podríamos especificar a = (string) "C". Esto crea un objeto de cadena y 'a' (variable sin tipo) simplemente apunta a él. No veo esto como más horrible que la cadena a = "C";
sturdytree
0

Un programa de computadora es un gráfico de nodos de proceso que describe lo que debe hacer una "máquina", representada por el tiempo de ejecución del lenguaje (extendido con kits de herramientas en la mayoría de los casos), en qué orden o bajo qué condiciones. Este gráfico está representado por un archivo de texto (o un montón de archivos de texto) escrito en un idioma específico y (parcial o totalmente) creado cuando el compilador / intérprete lee (deserializa) este archivo. Además, hay algunos entornos (UML o herramientas de generación de programas gráficos) en los que puede construir este gráfico y generar el código fuente en un idioma de destino.

¿Por qué digo esto? Porque esto lleva a la respuesta a tu pregunta.

El texto del programa es sus instrucciones sobre cómo la computadora debe resolver la tarea real, que contiene los pasos del proceso (condiciones, acciones) Y la estructura (qué componentes utiliza en la solución). Esto último significa que obtienes o creas algunas instancias de otros componentes, los pones en cuadros con nombre (variables) y los usas: accede a sus datos y servicios.

Algunos idiomas le dan cuadros uniformes, donde solo la etiqueta es importante, pero puede poner cualquier cosa en ellos, incluso puede usar una variable llamada "objetivo" para almacenar una "Persona" al principio y "Auto" al final de El mismo algoritmo. Otros requiere que cree cuadros "con forma", por lo tanto, diferentes para una Persona o un Coche, aunque todavía le permiten crear un "cuadro genérico" (Objeto Java, C / C ++ void *, Id. Objetivo C ...) y tíralo como quieras. Los idiomas mecanografiados le permiten expresar su estructura en detalles más finos, creando "contratos de tipo" para sus variables (aunque puede hackear esta limitación), mientras que los idiomas no tipificados apoyan este enfoque de "Seguramente sabré lo que he puesto en esa casilla esta vez". como el comportamiento predeterminado y único.

Ambos enfoques son viables, tienen su inteligencia compiladora, muchos libros de programación, prácticas y marcos escritos que los usan (y otras toneladas de libros sobre esos marcos) en diferentes niveles. Así que hoy la respuesta parece ser más una cuestión de gustos y conocimiento del equipo de programadores real que una declaración debidamente fundada, medida y verificada sobre si usar o no usar tipos.

Creo que no hace falta decir que prefiero las reglas a los trucos, especialmente para proyectos a largo plazo de grandes equipos (también conocidos como "serios"). Motivo: hasta donde yo sé, las causas más probables de un fracaso / deslizamiento de un proyecto de SW son: requisitos poco claros y diseño deficiente (80% por un estudio que conozco), y solo queda un pequeño porcentaje para la codificación real. Todas las reglas y contratos imponen un diseño más limpio, con visión de futuro y requieren que las decisiones se tomen antes y por las personas adecuadas. Los tipos significan reglas: menos "libertad" y "frescura": más preparación, pensamiento, estándares de codificación, trabajo en equipo controlado. Para mí, esto suena como un factor de éxito requerido, y también "hogar, dulce hogar".

Mis 2 centavos

Lorand Kedves
fuente
-3

AFIAK todos los idiomas con escritura dinámica son idiomas interpretados. Eso ya es muy ineficiente, agregar la ineficiencia del tipeo dinámico no será una gran pérdida de tiempo. Sin embargo, un lenguaje compilado no se referirá realmente a las cosas por su nombre cuando se esté ejecutando. (Salvo el uso ocasional de la reflexión .net o similar, características que son muy lentas en comparación con el lenguaje subyacente). La búsqueda de todos esos nombres será lenta, lenta, lenta.

Loren Pechtel
fuente
3
Sabes mal. Hay muchos compiladores para lenguajes de tipo dinámico, y algunos de ellos son bastante rápidos. Los ejemplos incluyen Common Lisp, Scheme y Erlang.
Ryan Culpepper
3
No existe tal cosa como un "lenguaje interpretado". Un lenguaje es un conjunto abstracto de reglas matemáticas. Un lenguaje no se compila ni se interpreta. Un idioma simplemente es. La compilación y la interpretación son rasgos de la implementación, no el lenguaje. Cada idioma puede implementarse con un intérprete, y cada idioma puede implementarse con un compilador. Y casi todos los lenguajes tienen implementaciones tanto interpretadas como compiladas, por ejemplo, hay intérpretes para C y compiladores para ECMAScript, Ruby y Python.
Jörg W Mittag
-4

Los lenguajes de tipo dinámico suelen promocionarse como "orientados a objetos". No son. Pueden estar orientados a la encapsulación, pero nunca a objetos. La orientación a objetos tiene que ver con los tipos.

"El niño monta la bicicleta de su hermano a la tienda de comestibles y compra una barra de pan de la tienda de comestibles". Usando la orientación a objetos, uno puede escribir inmediatamente un conjunto de clases (tipos) para describir este escenario del mundo real.

En un lenguaje de tipo dinámico, el escenario solo se puede representar de esta manera:

"El objeto monta el objeto de su objeto en el objeto y compra un objeto del objeto".

El poder de la orientación a objetos está en su capacidad de modelar el mundo en términos naturales, de modo que el desarrollador de software pueda usar ambos lados del cerebro para escribir software y resolver problemas más como un ser humano, menos como un programador de computadoras. Este poder está ausente en los idiomas de tipo dinámico.

La tipificación estática permite una mejor eficiencia de codificación, reutilización y mantenibilidad, porque los entornos de desarrollo integrados conocen los tipos de variables. Al conocer los tipos de variables, un IDE puede proporcionar la finalización automática para que el programador no tenga que volver a consultar la definición de clase para recordar si la propiedad del miembro se deletrea "backlightControl" o "backLightControl" o "bkLightCtrl".

La tipificación estática permite la refactorización automática, porque el IDE conoce cada lugar donde una variable contiene una instancia del objeto que se refactoriza.

La escritura estática permite una mayor reutilización y mantenibilidad. La escritura dinámica es mejor para el código desechable. Supongamos que un nuevo desarrollador entra en la calle y mira una pieza de código existente. Si el código se escribe estáticamente, el desarrollador puede, con dos clics del mouse, examinar la definición de clase de cada una de las variables involucradas, sabe para qué sirve la clase, sabe qué otros métodos y propiedades están disponibles. Si el código se escribe dinámicamente, el desarrollador debe usar la búsqueda global para descubrir qué está sucediendo.

Charlie Hand
fuente
2
Me temo que tengo que estar en desacuerdo con usted en la primera parte de su argumento. Los lenguajes de tipo dinámico suelen estar "orientados a objetos", pero no "orientados a clases". Esta es una distinción importante. En tu ejemplo, en realidad estás viviendo en una ilusión. Puede que tengas una Boyclase, pero dudo que pueda hacer todo lo que hace un "niño" del mundo real. Sin embargo, en su ejemplo dinámico (El objeto se monta ...), sabemos lo único importante acerca de este objeto "niño": puede montarse . Esa es la filosofía básica de los lenguajes de tipo dinámico. Tiene + ve sy -ve s. Cuál te gusta es tu opinión.
Chip