2009-05-18 35 views
28

Supongamos que tengo la clase siguiente:genéricos de mayor kinded en Java

public class FixExpr { 
    Expr<FixExpr> in; 
} 

Ahora quiero introducir un argumento genérico, haciendo abstracción sobre el uso de Expr:

public class Fix<F> { 
    F<Fix<F>> in; 
} 

Pero Eclipse doesn' t gusta esto:

El tipo F no es genérico; no puede ser parametrizada con argumentos < Fijar <F> >

Es esto posible en absoluto o que han pasado por alto algo que causa esta instancia específica para romper?

Información básica: en Haskell esta es una forma común de escribir funciones genéricas; Estoy intentando portar esto a Java. El argumento de tipo F en el ejemplo anterior tiene kind * -> * en lugar del tipo habitual *. En Haskell se ve así:

newtype Fix f = In { out :: f (Fix f) } 
+1

¿Cuál es el problema real que está tratando de resolver con esto? ¿No podría resolverse más fácilmente usando una plantilla? – KitsuneYMG

Respuesta

23

Creo que lo que estamos tratando de hacer no es simplemente compatible con los genéricos de Java. El caso más simple de

public class Foo<T> { 
    public T<String> bar() { return null; } 
} 

tampoco se compila con javac.

Dado que Java no sabe en tiempo de compilación qué es T, no puede garantizar que T<String> sea significativo.Por ejemplo, si ha creado un Foo<BufferedImage>, bar tendría la firma

public BufferedImage<String> bar() 

que es absurdo. Dado que no existe un mecanismo que lo obligue a crear una instancia única de Foo s con T s genérico, se niega a compilar.

5

el fin de pasar un parámetro de tipo, la definición de tipo tiene que declarar que acepta uno (que tiene que ser genérica ). Aparentemente, su F no es un tipo genérico.

UPDATE: La línea

F<Fix<F>> in; 

declara una variable de tipo F que acepta un parámetro de tipo, el valor de los cuales es Fix, que en sí acepta un parámetro de tipo, el valor de los cuales es F. F ni siquiera está definido en su ejemplo. Creo que se puede desee

Fix<F> in; 

que le dará una variable de tipo Fix (el tipo que definió en su ejemplo) a la que usted está pasando un parámetro de tipo con valor F. Como Fix se define para aceptar un parámetro de tipo, esto funciona.

ACTUALIZACIÓN 2: Vuelve a leer el título, y ahora creo que podrías estar intentando hacer algo similar al enfoque presentado en "Towards Equal Rights for Higher-Kinded Types" (alerta de PDF). Si es así, Java no lo admite, pero puedes probar Scala.

+0

¿Puede por favor elaborar un poco? He declarado que el tipo acepta un parámetro de tipo - Fix . ¿O no es esto lo que quieres decir? – Martijn

+0

Pero el tipo que ha elegido - > - no es genérico; solo funciona para la reparación - ¿Debería declararlo como tipo , seguramente? – sanbikinoraion

+0

Ese documento es interesante, ¡gracias! – Martijn

0

Parece que es posible que desee algo como: (. Consulte la clase Enum, y las preguntas sobre sus genéricos)

public class Fix<F extends Fix<F>> { 
    private F in; 
} 

+0

Hola Tom, esa solución parece realmente interesante, pero realmente no entiendo qué es lo que hace. ¿Eso no impone una jerarquía en F? No quiero hacer eso, todo lo que pido de F es que todavía recibe un argumento tipo para ser completo. – Martijn

+0

si ese es el caso, entonces no es posible en java hacer lo que quiera, el lenguaje no es lo suficientemente expresivo. – Chii

+0

Dice que F es un tipo de Fix. Debido a que Fix es genérico, se le debe dar el argumento genérico correcto. Tu pregunta es extremadamente abstracta. Entiendo que Haskell tiene un sistema de tipos complicado que es diferente al de Java. No es sorprendente que no haya un mapa 1: 1 para cada característica. –

24

Tal vez pueda probar Scala, que es un lenguaje funcional que se ejecuta en JVM, que admite genéricos de mayor calidad.


[EDITAR por Rahul G]

Así es como el ejemplo particular se traduce aproximadamente a Scala:

trait Expr[+A] 

trait FixExpr { 
    val in: Expr[FixExpr] 
} 

trait Fix[F[_]] { 
    val in: F[Fix[F]] 
} 
1

Sin embargo, hay maneras de codificar los genéricos higer-kinded en Java. Por favor, eche un vistazo al higher-kinded-java project.

Usando esto como una biblioteca, puede modificar el código de la siguiente manera:

public class Fix<F extends Type.Constructor> { 
    Type.App<F, Fix<F>> in; 
} 

probablemente debería añadir una anotación @GenerateTypeConstructor a su Expr clase

@GenerateTypeConstructor 
public class Expr<S> { 
    // ... 
} 

Esta anotación genera clase ExprTypeConstructor. Ahora puede procesar su solución de Expr así:

class Main { 
    void run() { 
     runWithTyConstr(ExprTypeConstructor.get); 
    } 

    <E extends Type.Constructor> void runWithTyConstr(ExprTypeConstructor.Is<E> tyConstrKnowledge) { 
     Expr<Fix<E>> one = Expr.lit(1); 
     Expr<Fix<E>> two = Expr.lit(2); 

     // convertToTypeApp method is generated by annotation processor 
     Type.App<E, Fix<E>> oneAsTyApp = tyConstrKnowledge.convertToTypeApp(one); 
     Type.App<E, Fix<E>> twoAsTyApp = tyConstrKnowledge.convertToTypeApp(two); 

     Fix<E> oneFix = new Fix<>(oneAsTyApp); 
     Fix<E> twoFix = new Fix<>(twoAsTyApp); 

     Expr<Fix<E>> addition = Expr.add(oneFix, twoFix); 
     process(addition, tyConstrKnowledge); 
    } 

    <E extends Type.Constructor> void process(
      Fix<E> fixedPoint, 
      ExprTypeConstructor.Is<E> tyConstrKnowledge) { 

     Type.App<E, Fix<E>> inTyApp = fixedPoint.getIn(); 

     // convertToExpr method is generated by annotation processor 
     Expr<Fix<E>> in = tyConstrKnowledge.convertToExpr(inTyApp); 

     for (Fix<E> subExpr: in.getSubExpressions()) { 
      process(subExpr, tyConstrKnowledge); 
     } 
    } 

}