deontólogo
Digamos que creamos una reimplementación de C, con la única diferencia de que se infieren los tipos. Aún sería necesario proporcionar clases de almacenamiento y modificadores (const, static, restrict, etc.), y restrinjamos nuestra atención a los programas C de un solo archivo por el momento. ¿Se podría hacer? ¿Cuáles son los principales impedimentos?
Algunas ideas sobre lo que podría causar problemas con la inferencia de tipos
- las estructuras con el mismo nombre de campo deberían eliminarse manualmente
- lo mismo para uniones con los mismos nombres de campo
-
casts probablemente necesitaría una anotación “desde”, algo así como
var i = (uint32_t -> uint64_t) *some_pointer;
Estos problemas requerirían un poco de anotación por parte del usuario, pero no deberían ser demasiado complicados, ¿hay algún problema grave que haga esta idea fuera del agua?
Editar: para aclarar, no estoy hablando de agregar genéricos o polimorfismo paramétrico, solo escriba inferencia para los tipos C existentes.
Editar 2014: cualquier persona interesada en este concepto puede querer investigar Óxido
Ciro Santilli OurBigBook.com
GCC 5.1 admite:
__auto_type
extensión, análoga a C++11auto
-
typeof
extensión, análoga a C++11decltype
/* Same as: double j = 0.5; */ typeof(1 + 0.5) j = 0.5; assert(j == 0.5);
Ejemplo del kernel de Linux: ¿Cómo funciona la macro de verificación de tipos del kernel de Linux?
-
_Generic
Palabra clave de C11: sintaxis y uso de muestra de _Generic en C11__auto_type i = 1; assert(_Generic((i), int: 1, default: 0));
-
Nada de esto implica inferencia de tipo en el sentido planteado en la pregunta (creo). C++11
auto
en particular, es extremadamente trivial y está muy lejos de la inferencia de tipo en el sentido de que la gente de cualquier otra comunidad normalmente usaría ese término.– Andreas Rossberg
30 de julio de 2015 a las 0:23
-
@AndreasRossberg ¿Puede dar un ejemplo de código mínimo de cómo se vería esa característica? en.wikipedia.org/wiki/C%2B%2B11#Type_inference dice que C ++ 11 tiene inferencia de tipos (lo sé, wikipedia … =)) Echaré un vistazo a rust y Hindley-Milner.
– Ciro Santilli OurBigBook.com
30 de julio de 2015 a las 5:55
-
La inferencia de tipos describe tradicionalmente la situación en la que la información de tipos no solo se recopila de abajo hacia arriba, sino que se deriva de usos. Ejemplo sencillo:
succ(n) { return n+1 }
si puedes inferir el tipo den
(y por lo tantosucc
) de su uso en una adición.– Andreas Rossberg
30 de julio de 2015 a las 6:34
C promueve algunos tipos automáticamente, lo que complica las cosas. Pero para que funcione, debe tener un par de diferencias adicionales con respecto a C tal como existe: en este momento, el estándar aún admite hasta cierto punto los programas K&R C heredados, y eso requiere que los tipos no especificados se manejen de maneras particulares. (Ver, por ejemplo, las reglas para parámetros de funciones en ausencia de prototipos. También solía ser posible especificar funciones y variables con No tipo, y estarían predeterminados a (int)
. (¿Cómo para las variables? Solo clase de almacenamiento. static foo;
)) Todo este manejo de tipos heredados tendría que eliminarse antes de poder agregar un nuevo mecanismo de tipos implícitos.
Inferir tipos de funciones polimórficas requeriría extensiones dramáticas al sistema de tipo C. Ejemplo
length(p) {
if (p == NULL) return 0;
else return 1 + length(p->next);
}
Este código tiene que funcionar en cualquier puntero a una estructura (o una unión) con un next
campo. Podría verificar el polimorfismo de filas, pero al menos, su nuevo sistema de tipos tendrá que ser mucho más expresivo que el sistema de tipos C.
Otro problema apremiante es la sobrecarga +
operación. ¿Qué tipo tiene por defecto? ¿Quieres que se sobrecargue en cualquier tipo numérico, como Haskell o C++? Si es así, más extensiones grandes al sistema de tipos.
La lección más grande es no hagas esto. Los méritos de C (como lenguaje, aparte de las muchas excelentes API disponibles en C) son
- Tienes control total sobre la representación de tus datos.
- Cualquier persona que inspeccione el código fuente puede predecir fácilmente los costos de tiempo y espacio.
Esta agenda no es realmente compatible con el polimorfismo, y el polimorfismo es un beneficio principal de la inferencia de tipos. Si lo que desea es la inferencia de tipos, elija uno de los muchos lenguajes excelentes (F#, Haskell, ML) que lo admiten de forma nativa.
-
Bueno, C no permite genéricos, por lo que en este caso (teóricamente) podría ver qué tipos de estructura estaban en el alcance que tenían un campo llamado next, y si había dos o más, requerir una anotación.
– deontólogo
29 de junio de 2011 a las 1:11
-
en mi humilde opinión, para C en sí mismo para seguir siendo un lenguaje funcional, necesita agregar nuevas formas de declarar tipos que permitan a un programador distinguir “número cuyo valor es de 0 a 4294967295” de “miembro del anillo algebraico de valores congruentes mod 4294967296”. Agregar el primero a cualquier otro tipo de número debería promover ambos al tamaño más conveniente lo suficientemente grande como para contener ambos; agregar un número a este último debería dar como resultado el mismo tipo de anillo algebraico.
– Super gato
23/10/2014 a las 16:01
-
No hay una buena razón por la que un procesador con registros de 64 bits no deba promover cálculos que involucren números para usar los 64 bits completos, pero por otro lado, una gran cantidad de código se romperá si
uint32_t
de repente deja de comportarse como un anillo algebraico abstracto. Agregar un medio para declarar que algo es de tipo (sintaxis hipotética)unsigned[int 32]
cuando lo primero es apropiado yunsigned [restrict 32]
cuando se necesita este último podría mejorar en gran medida la solidez del código y la independencia de la máquina al tiempo que reduce la necesidad de encasillamientos.– Super gato
23/10/2014 a las 16:05
-
Por cierto, con respecto al “control completo de los datos”, eso está terriblemente lejos de ser el caso, aunque algunas pequeñas adiciones al lenguaje (como las anteriores) solucionarían el problema en un 99,9 %. Otra mejora que me gustaría ver sería permitir que los tipos especifiquen el diseño de almacenamiento [e.g. have
unsigned [restrict uint8_t (0:8,8:8,16:8,24:8)]
indicar que el valor de 32 bits debe almacenarse utilizando cuatrouint8_t
valores de 8 bits cada uno, LSB primero, independientemente del tamaño de palabra de la máquina; si eso coincide con el orden de las palabras de la máquina, genial; si no, y si el tipo se utiliza en ununion
o…– Super gato
23/10/2014 a las 16:13
-
…se toma su dirección, se requeriría que el compilador use cualquier secuencia de turnos y almacenamientos necesarios para manejar el formato especificado. No es un gran cambio en el lenguaje, pero permitiría que muchos programas se escribieran de tal manera que fueran más concisos, robustos y de alto rendimiento (si una plataforma big-endian tiene una instrucción de intercambio de bytes en palabras, una el compilador para esa plataforma podría usarlo al cargar/almacenar el tipo entero anterior más fácilmente que si el tipo se leyera con un código como
value=(p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24))
)– Super gato
23/10/2014 a las 16:21
C tiene un conjunto complicado de reglas con respecto a la promoción y conversión de tipos que las personas encuentran confusas incluso cuando realmente pueden ver los tipos con los que están tratando. Sospecho que incluso si C tuviera inferencia de tipos, sería necesario declarar tipos para evitar errores ridículos cada tercera función.
es posible hacer alguno escriba inferencia en C. Eche un vistazo a esta herramienta: http://cuda.dcc.ufmg.br/psique-c. Puede escribir parte de un programa allí y reconstruirá las declaraciones de tipo que faltan. Por ejemplo, si lo alimentamos con una variación del programa de Norman:
int length(T p) {
if (p == NULL) return 0;
else return 1 + length(p->next);
}
Entonces psyche-c encuentra estas declaraciones:
#include <stdint.h>
#define NULL ((void*)0)
typedef int bool;
bool false = 0;
bool true = 1;
typedef struct T {struct T* next;}* T;
Este tipo de reconstrucción de tipos es útil para completar el código, por ejemplo.
Algunas situaciones en las que, creo, los tipos inferidos no podrían usarse:
- punteros necesitan un tipo definido
- las matrices necesitan un tipo definido
- los parámetros de función necesitan un tipo
Creo que podría funcionar en algunos casos, en su mayoría variables locales simples.
Incluso algo tan simple como calcular una suma de comprobación necesita un tipo definido
crc8 = 0x00; /* 8 bits; cf uint8_t crc8 = 0; */
crc32 = 0x00000000; /* 32 bits; cf uint32_t crc32 = 0; */
Se podría hacer, para un uso limitado.
Probablemente estoy siendo lento; ¿Puedes dar un ejemplo para tu primer punto?
–Oliver Charlesworth
28 de junio de 2011 a las 22:31
Lo que estás describiendo suena más como un sistema de tipo dinámico. La inferencia de tipos es una idea diferente en conjunto.
–Jeff Mercado
28 de junio de 2011 a las 22:32
No siento que tenga la experiencia para enviar una respuesta completa, pero mi intuición dice que esto es perfectamente factible, al menos para un lenguaje similar a C que cubre quizás el 90% -95% de C. Ya tenemos una inferencia de tipo viable para Python (consulte el proyecto ShedSkin), por lo que me imagino que se podrían usar técnicas similares para una versión de C.
– Juan Y.
28 de junio de 2011 a las 22:34
@pmg: la inferencia de tipo no tiene nada que ver con conversiones de tipo automáticas extrañas. De hecho, interactúa bastante mal con ellos.
– mandril
28 de junio de 2011 a las 22:39
@pmg: en C tal como está ahora, es un comportamiento indefinido para su caso específico y la adición de puntero en el caso general. Esto no cambiaría; la pregunta no es sobre agregar moldes implícitos, sino simplemente inferir tipos no especificados. De hecho, no estoy seguro de cómo alguien está leyendo pato escribiendo esto; eso es bastante más amplio que esta propuesta.
– geekosaur
28 de junio de 2011 a las 22:40