2010-11-06 25 views
12

Los tipos estructurales son uno de esos "guau, guay!" características de Scala. Sin embargo, para cada ejemplo en el que pueda pensar dónde podrían ayudar, las conversiones implícitas y la composición de mixin dinámico a menudo parecen mejores coincidencias. ¿Cuáles son algunos usos comunes para ellos y/o consejos sobre cuándo son apropiados?¿Usos prácticos para tipos estructurales?

+0

Esto todavía es un problema que trato de entender mejor, y hasta ahora no he visto un caso convincente para los tipos estructurales. ¿Las implicaciones no serían una mejor opción incluso para el ejemplo canónico 'close()'? –

+0

Ver también http://stackoverflow.com/questions/3498954/why-do-compile-time-generative-techniques-for-structural-typing-prevent-separate. –

Respuesta

13

Aparte de los raros casos de clases que proporcionan el mismo método, pero no están relacionados ni tampoco implementar una interfaz común (por ejemplo, el método close()-Source, por su parte, no se extiende Closeable), no encuentro usar para tipos estructurales con su restricción actual. Si fueran más flexibles, sin embargo, podría también escribir algo como esto:

def add[T: { def +(x: T): T }](a: T, b: T) = a + b 

que manejar cuidadosamente los tipos numéricos. Cada vez que creo que los tipos estructurales pueden ayudarme con algo, choco con ese muro en particular.

EDITAR

Sin embargo me parece inútil tipos estructurales mí mismo, el compilador , sin embargo, se utiliza para manejar clases anónimas. Por ejemplo:

implicit def toTimes(count: Int) = new { 
    def times(block: => Unit) = 1 to count foreach { _ => block } 
} 

5 times { println("This uses structural types!") } 

El objeto resultante de (la implícita) toTimes(5) es de tipo { def times(block: => Unit) }, es decir, un tipo estructural.

No sé si Scala hace eso para cada clase anónima, quizás sí. Por desgracia, esa es una razón por la que hacer chulo mi biblioteca de esa manera es lento, ya que los tipos estructurales usan la reflexión para invocar los métodos. En lugar de una clase anónima, uno debe usar una clase real para evitar problemas de rendimiento en proxeneta de mi biblioteca.

+0

corrigió algunos pequeños errores en tu muestra :) –

+0

Intenté que tu primer ejemplo funcionara - Lo mejor que se me ocurrió fue def agregar [T <% {def + (x: T): R}, R] (a: T, b: T): R = a + b, pero el compilador se quejó sobre el tipo estructural que se refiere a R – Collin

+0

@Vasil Me estoy volviendo descuidado ... :-(¡Gracias por la ayuda! –

3

Los tipos estructurales son construcciones muy interesantes en Scala. Los he usado para representar múltiples tipos no relacionados que comparten un atributo sobre el que quiero realizar una operación común sin un nuevo nivel de abstracción.

He escuchado un argumento en contra de los tipos estructurales de las personas que son estrictas acerca de la arquitectura de una aplicación. Consideran que es peligroso aplicar una operación común en todos los tipos sin un rasgo asociativo o tipo parental, porque luego se deja la regla de qué tipo debe aplicarse el método a los extremos abiertos. El ejemplo de Daniel close() es acertado, pero ¿qué pasa si tienes otro tipo que requiere un comportamiento diferente? Alguien que no entiende la arquitectura podría usarla y causar problemas en el sistema.

2

Creo que los tipos estructurales son una de estas características que no necesita tan a menudo, pero cuando lo necesita, lo ayuda mucho. Un área donde los tipos estructurales realmente brillan es la "adaptación", p. cuando necesita unir varias piezas de software para las que no tiene un código fuente y que no estaban destinadas a ser reutilizadas. Pero si te encuentras utilizando tipos estructurales mucho, probablemente lo estés haciendo mal.

[Editar]

De implícitos curso son a menudo el camino a seguir, pero hay casos en que no se puede: Imagine que tiene un objeto mutable puede modificar con los métodos, pero que esconde una parte importante de su estado, una especie de "caja negra". Entonces tienes que trabajar de alguna manera con este objeto.

Otro caso de uso para tipos estructurales es cuando el código se basa en convenciones de nomenclatura sin una interfaz común, p. en código generado por la máquina.En el JDK también podemos encontrar tales cosas, como el par StringBuffer/StringBuilder (donde las interfaces comunes Appendable y CharSequence son manera en general).

+0

Gracias. ¿Podría comentar cuándo los tipos estructurales son más efectivos que las conversiones implícitas cuando se trata de bibliotecas de código fuente cerradas? –

+0

Vea mi [Editar] – Landei

+1

¿Podría explicar el "negro"? cuadro "ejemplo" ¿Cómo ayuda la escritura estructural en este caso de uso? –

1

Los tipos estructurales proporcionan algunos beneficios de los lenguajes dinámicos a un lenguaje estáticamente vinculado, específicamente el acoplamiento libre. Si desea un método foo() para llamar a los métodos de instancia de la clase Bar, no necesita una interfaz o clase base que sea común tanto para foo() como para Bar. Puede definir un tipo estructural que foo() acepta y cuyo Bar no tiene ninguna pista de existencia. Siempre que Bar contenga métodos que coincidan con las firmas de tipo estructural, foo() podrá llamar.

Es genial porque puede poner foo() y Bar en librerías distintas, completamente no relacionadas, es decir, sin un contrato de referencia común. Esto reduce los requisitos de enlace y, por lo tanto, contribuye aún más al acoplamiento flexible.

En algunas situaciones, un tipo estructural puede ser utilizado como una alternativa al modelo Adapter, ya que ofrece las siguientes ventajas:

  • identidad de objeto se conserva (no hay ningún objeto independiente de la instancia de adaptador, al menos en el nivel semántico).
  • No necesita crear instancias de un adaptador, solo pase una instancia de Bar al foo().
  • No es necesario implementar métodos de envoltura: simplemente declare las firmas necesarias en el tipo de estructura.
  • El tipo estructural no necesita conocer la clase o interfaz de instancia real, mientras que el adaptador debe saber Bar para que pueda invocar sus métodos. De esta forma, un único tipo estructural puede usarse para muchos tipos reales, mientras que con el adaptador es necesario codificar múltiples clases, una para cada tipo real.

El único inconveniente de los tipos estructurales en comparación con los adaptadores es que no se puede utilizar un tipo estructural para traducir las firmas de métodos. Entonces, cuando las firmas no coinciden, debe usar adaptadores que tengan alguna lógica de traducción. En particular, no me gusta codificar los adaptadores "inteligentes" porque en muchos casos son más que simples adaptadores y causan una mayor complejidad. Si un cliente de la clase necesita algún método adicional, prefiero simplemente agregar dicho método, ya que generalmente no afecta la huella.

Cuestiones relacionadas