2012-02-12 27 views
7

¿Qué técnicas puedo usar en Scala para manejar listas de parámetros de tipo largo?Scala: cómo trabajar con listas de parámetros de tipo largo

Estoy trabajando en un pequeño marco para ejecutar varios tipos de juegos con diferentes entornos simulados. Estoy tratando de mantener ciertas partes del framework relativamente genéricas, así que estoy presentando varios tipos como parámetros de tipo, como el estado del entorno, el resultado del juego, etc.

Todo funciona bastante bien y lo hago obtenga los beneficios deseados de un marco genérico pero seguro para el tipo. Pero las firmas de tipos han crecido hasta el punto de hacer que el código sea bastante difícil de leer y la refacturación se ha vuelto bastante engorrosa. La firma del Simulador de nivel superior tiene ocho parámetros de tipo y muchos de los tipos principales tienen de tres a cinco. Los errores individuales del tipo de compilador, ya que enumeran los tipos de clase o parámetros de función (que por supuesto también tienen parámetros de tipo), parecen ejecutarse regularmente en cien líneas.

De vez en cuando, pero muy raramente, puedo omitir los parámetros de tipo, p. Ej. en constructores Pero en la mayoría de los casos, al menos uno de los tipos no se deducirá, así que termino insertando la firma de tipo completa.

Obviamente esto no es ideal y estoy buscando formas de resolver este problema. ¡Cualquier consejo sería apreciado!

+6

pocas líneas de código de ejemplo siempre son apreciados;) – agilesteel

+0

Es un problema muy general. No estoy seguro de que el código de muestra agregue mucho valor. Son solo las cosas habituales: rasgos, clases, funciones, cada uno con parámetros de tipo, y campos/miembros también con parámetros de tipo. –

Respuesta

8

Considere el caso cuando tiene un grupo de parámetros de tipo que están estrechamente relacionados; los pasas juntos como argumentos tipo. En este ejemplo, este grupo es A y B.

trait X[A, B, C] { 
    new Y[A, B, Int] {} 
} 

trait Y[A, B, D] { 
    def a: A = sys.error("") 
} 

puede empaquetar estos dos tipos en un parámetro de tipo individual con una miembros de tipo de tipo de alias que contienen:

type AB = { type A; type B } 
trait Y[ab <: AB, D] { 
    def a: ab#A = sys.error("") 
} 
trait X[ab <: AB, C] { 
    new Y[ab, C] {} 
} 
new X[{ type A=Int; type B=Int}, String] {} 
+0

¡Oooh, eso está bien! Voy a intentarlo lo antes posible! –

+0

También conserva la capacidad de usar varianza con este enfoque. – retronym

+1

¿Cómo especifico la varianza? En su ejemplo, p. si Y fue originalmente el rasgo Y [+ A, -B]? –

10

Dos soluciones vienen a la mente.

  1. Use alias de tipo.

    scala> class Foo[A, B, C, D, E] 
    defined class Foo 
    
    scala> type Bar[A] = Foo[A, Int, Int, Int, Float] 
    defined type alias Bar 
    
    scala> new Bar[String] 
    res23: Foo[String,Int,Int,Int,Float] = [email protected] 
    
  2. Uso abstract type members en lugar de los parámetros de tipo.

    scala> class Bar { 
        | type A 
        | type B <: AnyVal 
        | type C 
        | } 
    defined class Bar 
    
    scala> new Bar { 
        | type A = String 
        | type B = Int 
        | type C = Int 
        | } 
    res24: Bar{type A = String; type B = Int; type C = Int} = [email protected] 
    
    scala> trait Baz { 
        | type A = String 
        | } 
    defined trait Baz 
    
    scala> new Bar with Baz { 
        | type B = Int 
        | type C = String 
        | } 
    res25: Bar with Baz{type B = Int; type C = String} = [email protected] 
    
    scala> null.asInstanceOf[res25.A] 
    res26: res25.A = null 
    
    scala> implicitly[res25.A =:= String] 
    res27: =:=[res25.A,String] = <function1> 
    

Es posible que desee compartir algo de código con nosotros para que podamos dar un consejo más específico.

+0

La primera respuesta que ya uso; Sería incluso mejor si de alguna manera pudieras usar el alias de tipo definido para acceder al constructor del tipo subyacente. El segundo es interesante; Voy a explorar esto. –

Cuestiones relacionadas