2011-08-18 21 views
8

Estaba implementando una biblioteca de tipado dinámico para D cuando me encontré con un problema interesante.Usando el tipado dinámico en D, un lenguaje estáticamente tipado

En este momento, he logrado crear una función llamada dynamic() que devuelve una versión dinámica de un objeto.

Por ejemplo:

import std.stdio, std.dynamic.core; 

class Foo 
{ 
    string bar(string a) { return a ~ "OMG"; } 
    int opUnary(string s)() if (s == "-") { return 0; } 
} 

void main(string[] argv) 
{ 
    Dynamic d = dynamic(new Foo()); 
    Dynamic result = d.bar("hi"); 
    writeln(result); // Uh-oh 
} 

El problema que he encontrado a través de es el hecho de que writeln intenta utilizar tiempo de compilación reflexión para encontrar la manera de tratar result.

¿Qué es lo primero que intenta? isInputRange!(typeof(result))

El problema es que devuelve verdadero! ¿Por qué? Porque tengo que suponer que todos los miembros que necesita existen, a menos que pueda demostrar lo contrario en tiempo de ejecución, lo cual es demasiado tarde. Entonces el programa intenta llamar al front, popFront y empty en result, bloqueando mi programa.

No se me ocurre una manera de arreglar esto. ¿Alguien tiene alguna idea?

Respuesta

1

lo que es malo en usar std.variant que implementa todo lo necesario para tipado dinámico (junto con un poco de azúcar sintáctico)

+1

'std.variant' no es compatible con tipos que tienen campos arbitrarios. –

+0

@cyber, ¿qué quieres decir? –

+0

OP quiere crear un objeto donde 'obj.anything' sea válido en tiempo de compilación (aunque puede que no sea válido en tiempo de ejecución). Nada en 'std.variant' permite esto, como he visto. –

1

Podría dar una sobrecarga de isInputRange? Algo como esto (tenga en cuenta que no he mirado en la implementación de isInputRange):

template isInputRange(T : Dynamic) { 
    enum isInputRange = false; 
} 

Si esto es proporcionado por su dynamic.core, creo que esta sobrecarga debe ser elegido antes de la std uno lib.

+1

Correcto, pero el problema es que esto requeriría conocer todo tipo de prevención por adelantado. Obviamente, no funciona para quien termine usando mi biblioteca ... – Mehrdad

+0

Desafortunadamente este truco no funciona, std.stdio no puede retomar la especialización. – Lutger

0

Para el caso general, Dynamic tiene que aceptar cualquier búsqueda de método en tiempo de compilación, como usted dijo. Supongamos por un momento que puede evitar que el predicado isInputRange se evalúe como verdadero, ahora se generará el código incorrecto cuando intente crear un Dynamic a partir de un rango de entrada.

No creo que esto sea reparable, al menos no de forma general. En este caso particular, la mejor solución que puedo pensar es que Dynamic proporciona su propia versión de toString, y writeln preferiría eso sobre la especialización de inputRange. Creo que writeln no hace esto en este momento, al menos no para las estructuras, pero probablemente debería.

Otro compromiso sería no permitir algunos métodos como popFront en la restricción opDispatch, en cambio, Dynamic proporcionaría opIndex o un objeto miembro para acceder a estos casos especiales. Esto podría no ser tan malo como parece, porque los casos especiales son raros y su uso daría lugar a un error obvio del compilador.

Creo que la mejor manera de salvar este tipo de resolución de método para Dynamic es corregir writeln y aceptar que Dynamic no funcionará con todo el código de plantilla.

+0

El problema al hacer que writeln prefiera toString sobre isInputRange es que cada clase hereda un método toString genérico de Object, que simplemente emite el nombre de la clase. Por lo tanto, si writeln se cambiara, tendría que tratar las estructuras y las clases de forma diferente. – tgehr

2

Está tratando de hacer que dos conceptos fundamentalmente diferentes funcionen en conjunto, a saber, plantillas y tipeo dinámico. Las plantillas se basan mucho en el tipado estático, isInputRange funciona comprobando qué atributos o métodos tiene un tipo. Su tipo dinámico se trata como si tuviera cada atributo o método en tiempo de compilación, ergo se trata como cumpliendo cada interfaz estática de pato-tipado. Por lo tanto, para que Dynamic funcione en un entorno estáticamente tipado, debe proporcionar más información estática en algunos lugares.

Algunas soluciones que veo:

  1. proporcionan sus propias implementaciones de tipos dinámicos para las funciones de uso intensivo. Todo el problema que está teniendo es causado por el hecho de que está tratando de usar funciones genéricas que asumen tipado estático con tipos dinámicos.

  2. explícitamente hacen dinámico un rango de char, y se preocupan por la conversión a la cadena de los datos subyacentes usted mismo. (Debería tener un método personalizado toString de todos modos si el problema isInputRange no existiría, porque de lo contrario su resultado sería nuevamente de tipo Dinámico). Esto probablemente haría writeln (d); trabajo.

  3. proporcionan envolturas para dinámicas que le permiten pasar tipos dinámicos a varias funciones de plantilla. (Esos solo mostrarían una interfaz estática y reenviar todas las llamadas a Dynamic).

Ej:

Dynamic d; 
// wrap d to turn it into a compile-time input range (but NOT eg a forward range) 
Dynamic d2=dynamic(map!q{a*2}(dynInputRange(d))); 
// profit 

4. Agregue una plantilla de miembro a Dynamic, que permite desactivar estáticamente algunos nombres de funciones de miembros.

Ej:

static assert(!isForwardRange!(typeof(d.without!"save"))); 
0

¿Usted ha mirado en std.variant?

import std.stdio, std.variant; 

class Foo { 
    string Bar(string a) { 
     return a ~ " are Cool!"; 
    } 
} 

void main() { 
    Variant foo = new Foo(); 
    Variant result = foo.peek!Foo.Bar("Variants"); 

    writeln(result); // Variants are Cool! 
} 

http://www.d-programming-language.org/phobos/std_variant.html

Cuestiones relacionadas