2009-02-26 20 views
10

Me gustaría saber cómo funcionan los tipos del miembro en Scala, y cómo debo asociar tipos.Mecanografíe los parámetros contra tipos del miembro en Scala

Un enfoque es hacer que el tipo asociado sea un parámetro de tipo. Las ventajas de este enfoque es que puedo prescribir la varianza del tipo, y puedo estar seguro de que un subtipo no cambia el tipo. Las desventajas son que no puedo inferir el parámetro de tipo del tipo en una función.

El segundo enfoque es hacer que el tipo asociado sea un miembro del segundo tipo, que tiene el problema de que no puedo prescribir límites en los tipos asociados de los subtipos y, por lo tanto, no puedo usar el tipo en los parámetros de función (cuando x: X, X # T podría no estar en cualquier relación con xT)

un ejemplo concreto sería:

tengo un rasgo para DFAs (podría ser sin el parámetro de tipo)

trait DFA[S] { /* S is the type of the symbols in the alphabet */ 
    trait State { def next(x : S); } 
    /* final type Sigma = S */ 
} 

y quiero crear una función para ejecutar este DFA a través de una secuencia de entrada, y quiero

  • la función debe tomar nada <% Seq[alphabet-type-of-the-dfa] tipo secuencia de entrada
  • función de la persona que llama no es necesario especificar los parámetros de tipo, todo debe inferirse
  • que había como la función a llamar con el tipo de DFA concreto (pero si hay una solución donde la función no tendría un parámetro de tipo para el DFA, está bien)
  • los tipos de alfabeto no deben restringirse (es decir. tiene que haber un DFA para Char, así como para una clase definida por el usuario aún desconocido)
  • la DFA con diferentes tipos alfabeto no son subtipos

He intentado esto:

def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = .... 

funciona este , excepto que el tipo S no se infiere aquí, así que tengo que escribir la lista de parámetros de tipo completo en cada sitio de llamadas.

def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]](... same as above 

esto no funcionaba (referencia circular no válido para escribir D ??? (¿qué es?))

También he eliminado el parámetro de tipo, creé un tipo abstracto Sigma y trató de unión de ese tipo en las clases concretas runDFA se vería

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]](... same as above 

pero esto corre inevitablemente en problemas como la "coincidencia de tipos: se espera dfa.Sigma, consiguió D#Sigma"

¿Alguna idea? ¿Punteros?

Editar:

Como las respuestas indican que no hay una forma sencilla de hacer esto, alguien podría elaborar más sobre por qué es imposible y lo que tendría que ser cambiado por lo que funcionó?

Las razones por las que quiero que runDFA sea una función gratuita (no un método) es que quiero otras funciones similares, como la minimización de autómatas, operaciones regulares de lenguaje, conversiones de NFA a DFA, factorización de lenguaje etc. y tener todas esto dentro de una clase es justo contra casi cualquier principio del diseño OO.

+0

Guau, pensé que querías decir esto: http://www.huygens-fokker.org/scala/ – MusiGenesis

Respuesta

3

En primer lugar, no es necesario la parametrización SQ <% Sec [ S]. Escriba el parámetro del método como Seq [S]. Si SQ <% Seq [S] entonces cualquier instancia de la misma se puede convertir implícitamente a Seq [S] (eso es lo que significa <%), de modo que cuando se pasa como Seq [S], el compilador insertará automáticamente la conversión.

Además, lo que Jorge dijo acerca de los parámetros de tipo en D y lo convirtió en un método en DFA. Debido a la forma en que funcionan las clases internas en Scala, recomienda encarecidamente poner runDFA en DFA. Hasta que funcione la escritura dependiente de la ruta, lidiar con las clases internas de alguna clase externa puede ser un poco molesto.

Así que ahora usted tiene

trait DFA[S]{ 
    ... 

    def runDFA(seq : Seq[S]) = ... 
} 

Y runDFA es, de repente, en lugar fácil inferir parámetros de tipo para: No tiene ninguna.

3

La inferencia del tipo de Scala a veces deja mucho que desear.

¿Hay algún motivo por el que no pueda tener el método dentro de su rasgo de DFA?

def run[SQ <% Seq[S]](seq: SQ) 

Si no necesita el parámetro D posterior, también puede intentar definir su método sin ella:

def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ... 
0

Algo de información útil sobre cómo los dos difiere:

Desde el lo informe guide:

Sin parámetros de tipo no se puede hacer tipos dependientes, por ejemplo

trait Generic[A] { 
    type Repr 
    def to(value: A): Repr 
    def from(value: Repr): A 
} 

import shapeless.Generic 
def getRepr[A](value: A)(implicit gen: Generic[A]) = 
    gen.to(value) 

Aquí el tipo devuelto por to depende del tipo de entrada A (porque el implícito proporcionado depende de A):

case class Vec(x: Int, y: Int) 
case class Rect(origin: Vec, size: Vec) 
getRepr(Vec(1, 2)) 
// res1: shapeless.::[Int,shapeless.::[Int,shapeless.HNil]] = 1 :: 2 :: 
    HNil 
getRepr(Rect(Vec(0, 0), Vec(5, 5))) 
// res2: shapeless.::[Vec,shapeless.::[Vec,shapeless.HNil]] = Vec(0,0) 
    :: Vec(5,5) :: HNil 

sin miembros de tipo esto sería imposible:

trait Generic2[A, Repr] 
def getRepr2[A, R](value: A)(implicit generic: Generic2[A, R]): R = 
    ??? 

Nos hubiera tenido que pasar el valor deseado de Repr a getRepr como parámetro tipo, efec tivamente hacer getRepr inútil. El resultado intuitivo de esto es que los parámetros de tipo son útiles como "entradas" y los miembros de tipo son útiles como "salidas".

Consulte la información guide para más detalles.

Cuestiones relacionadas