¿En qué situaciones se deben preferir los tipos abstractos sobre los parámetros tipo?Tipos abstractos versus parámetros de tipo
Respuesta
Para añadir a mi previous answer on Abstract type vs. parameters, también tiene las JESSE EICHAR's recent blog post (2010, 3 de mayo) poner de relieve algunas diferencias clave:
trait C1[A] {
def get : A
def doit(a:A):A
}
trait C2 {
type A
def get : A
def doit(a:A):A
}
En C2
caso, el parámetro está "enterrado" (como un resumen interna tipo).
(excepto, como retronym pone, no está realmente enterrado, ver más abajo)
Mientras que con el tipo genérico, se menciona explícitamente el parámetro, ayudando a otras expresiones para saber qué tipo que se supone que se utiliza
Así (C1: parámetro):
//compiles
def p(c:C1[Int]) = c.doit(c.get)
compila, pero se exponga explícitamente el 'A
' tipo que desea utilizar.
Y (C2: Tipo Resumen):
// doesn't compile
def p2(c:C2) = c.doit(c.get)
<console>:6: error: illegal dependent method type
def p2(c:C2) = c.doit(c.get)
^
No compila porque 'A
' nunca es mencionado en la definición p2, por lo doit
no conoce al tipo de compilación lo que se supone que volver .
Al utilizar tipo abstracto y querer evitar cualquier "tipo de fuga" a la interfaz (es decir, con ganas de exponer lo que 'A
' en realidad es), puede especificar un tipo muy genérico como un cambio de p2 :
// compiles because the internals of C2 does not leak out
def p(c:C2):Unit = c.doit(c.get)
O puede "arreglar" este tipo directamente en la función doit
:
def doit(a:A):Int
en lugar de def doit(a:A):A
, lo que significa:
def p2(c:C2) = c.doit(c.get)
compilará (incluso si p2 no menciona ningún tipo de retorno)
último (retronym 's comentario) puede especificar A
ya sea de forma explícita mediante el refinado parámetro C2 Resumen:
scala> def p2(c:C2 { type A = Int }): Int = c.doit(c.get)
p2: (c: C2{type A = Int})Int
O por agregando un parámetro de tipo (y refinando el tipo abstracto C2 con él!)
scala> def p2[X](c:C2 { type A = X }): X = c.doit(c.get)
p2: [X](c: C2{type A = X})X
tan abstracto se recomiendan:
- Cuando se desea ocultar una definición exacta de un miembro de tipo de código de cliente, utilice el tipo abstracto como en
C2
(pero tenga cuidado con la definición de función usandoC2
) - Cuando desee sobrescribir el tipo de forma coherente en las subclases de
C2
, utilizar tipo abstracto (con extracción de tipo acotado) - Cuando se quiere mezclar en las definiciones de los tipos
C2
a través de los rasgos, utilizar tipo abstracto (que no tendrá 'A
' para hacer frente a la hora de mezclar conC2
su clase: se mezclan solamenteC2
)
Para el resto, donde sencilla tipo de instancias es necesario, parámetros de uso.
(si usted sabe que no será necesaria una extensión, pero todavía tiene que manejar varios tipos: eso es lo que los tipos de parámetros son para)
retronym añade:
Los diferencias principales están
- varianza:
C2
sólo puede ser invariantes enA
, - la forma en que los miembros de tipo pueden ser overriden selectivamente en un subtipo (mientras que los parámetros de tipo deben volver a declarar y se pasan al supertipo)
(como illustrating here:
trait T1 {
type t
val v: t
}
trait T2 extends T1 {
type t <: SomeType1
}
trait T3 extends T2 {
type t <: SomeType2 // where SomeType2 <: SomeType1
}
class C extends T3 {
type t = Concrete // where Concrete <: SomeType2
val v = new Concrete(...)
}
)
No está realmente enterrado: 'def p2 (c: C2 {tipo A = Int}): Int = c.doit (c.get)'. O bien: 'def p2 [X] (c: C2 {tipo A = X}): X = c.doit (c.get)'. Las principales diferencias son la varianza ('C2' solo puede ser invariante en' A') y la forma en que los miembros de tipo pueden ser selectivamente anulados en un subtipo, mientras que los parámetros de tipo deben ser redeclarados y pasados al supertipo. – retronym
@retronym: * "Las principales diferencias son la varianza ... y la forma en que los miembros de tipo pueden ser selectivamente anulados" * ¿Eso significa que todo lo que es posible hacer con uno de ellos es posible hacer con el otro? Excepto por los dos aspectos que mencionas. La única otra diferencia es la sintaxis? – Lii
- 1. Mezclando parámetros de tipo y tipos abstractos en scala
- 2. rasgos y tipos abstractos
- 3. Uso de tipos estructurales Scala con tipos abstractos
- 4. Abstract Parámetros Tipos/Tipo de Scala
- 5. Django Modelos abstractos versus Python simples mixins vs Python ABCs
- 6. constructores de la clase Scala y tipos abstractos
- 7. parámetros de tipos múltiples en las clases de tipo haskell
- 8. Resumiendo sobre tipos de parámetros de plantilla sin tipo
- 9. ¿Es posible usar evidencia implícita para forzar la compatibilidad de tipo estático entre tipos abstractos?
- 10. C parámetros # tipo de especificación
- 11. parámetros de tipo genérico circular
- 12. Parámetros de plantilla sin tipo
- 13. Desventajas del sistema de tipo Scala versus Haskell?
- 14. Funciones con los tipos de parámetros genéricos
- 15. ¿Cómo usar el tipeo de Scala, los tipos abstractos, etc. para implementar un tipo de uno mismo?
- 16. de tipo no Parámetros
- 17. No se pueden definir métodos estáticos en tipos abstractos. ¿Cómo trabajar?
- 18. atributos abstractos en Java
- 19. Listas en Haskell: tipo de datos o tipo de datos abstractos?
- 20. tipos de parámetros de función y =>
- 21. Parámetros de tipo de parámetro de Scala
- 22. Clase abstracta sin métodos abstractos
- 23. Diferencia entre los tipos de parámetros
- 24. Escriba restricciones de parámetros para las instancias de tipos de clase con tipo * -> *
- 25. funciones variables de tipo seguro con parámetros del mismo tipo
- 26. ¿Los métodos abstractos son virtuales?
- 27. Mensaje o un tipo que tiene MessageContractAttribute y otros parámetros de diferentes tipos
- 28. EF4 y múltiples niveles abstractos
- 29. Métodos abstractos en Python
- 30. pymssql versus pyodbc versus adodbapi versus ...
¿No es ya discutido en http://stackoverflow.com/questions/1154571/scala-abstract-types-vs-generics/1154727#1154727? – VonC
@VonC: Vi esa respuesta, pero no la encontré satisfactoria. –
Intento ilustrarlo a continuación, con una publicación reciente de Jesse EICHAR. – VonC