Estoy diseñando una jerarquía de clases, que consiste en una base de clase, junto con varios rasgos. La clase base proporciona implementaciones predeterminadas de varios métodos, y los rasgos de reemplazar selectivamente ciertos métodos a través de abstract override, así como a los hechos como apilable rasgos/mixins.

Desde la perspectiva del diseño, esto funciona bien, y se asigna al dominio de lo que yo puedo añadir una función de filtrado de aquí (un rasgo) con un predicado desde aquí (otro rasgo) etc.

Sin embargo, ahora me gustaría que algunos de mis rasgos a tomar parámetros implícitos. Estoy feliz de que este todavía tiene sentido desde una perspectiva de diseño, y de no resultar confusa en la práctica. Sin embargo, no puedo convencer al compilador a correr con ella.

El núcleo del problema parece ser que no puedo proporcionar argumentos de constructor para un rasgo, tal que puedan ser marcados de forma implícita. Hace referencia al parámetro implícito dentro de un método de aplicación falla al compilar con el esperado «no se puede encontrar el valor implícito de los mensajes»; traté de «propagar» el implícita de la etapa de construcción (donde, en la práctica, siempre en el ámbito) a estar disponible dentro del método a través de

implicit val e = implicitly[ClassName]

pero (como sin duda muchos de ustedes esperan) que definición no pudo con el mismo mensaje.

Parece que el problema aquí es que no me puedo convencer de que el compilador de la etiqueta de la firma del rasgo a sí mismo con un implicit ClassName de la bandera, y la fuerza de llamadas (es decir, aquellos que mezcla el rasgo en un objeto) para proporcionar el implícito. Actualmente mis contactos telefónicos son hacerlo, pero el compilador no es la comprobación en este nivel.


¿Hay alguna forma de marcar un rasgo requieren ciertas implicits estar disponibles en el momento de construcción?

(Y si no es esto simplemente no se ha implementado todavía, o es que hay una razón más profunda de por qué esto es poco práctico?)

5 Comentarios

  1. 16

    Realidad, he querido que esta bastante a menudo antes, pero sólo se le ocurrió esta idea. Usted puede traducir

    trait T(implicit impl: ClassName) {
      def foo = ... //using impl here
    }

    a [EDITADO: versión original no proporcionar acceso a los implícitos, por otros métodos]

    trait T {
      //no need to ever use it outside T
      protected case class ClassNameW(implicit val wrapped: ClassName)
    
      //normally defined by caller as val implWrap = ClassNameW 
      protected val implWrap: ClassNameW 
    
      //will have to repeat this when you extend T and need access to the implicit
      import implWrap.wrapped
    
      def foo = ... //using wrapped here
    }
    • No se que hacer la llamada de definir explícitamente implWrap pesar de que en el objeto anónimo, ya que es un campo de resumen en el rasgo? (Si no, no entiendo cómo se establece; ¿te importaría explicar?)
    • Sí, pero ver el comentario: si quiere utilizar el implícita, se puede escribir sólo val implWrap = ClassNameW. No veo una mejor manera de hacerlo: como usted menciona en la pregunta, los rasgos no tengo alguna los parámetros del constructor (que podría ser marcados implícita).
    • Por supuesto, yo estaría muy feliz de ver a una mejor solución.
    • Tengo que hacer una cosa similar, pero el uso de la inferencia de tipos para que me ayude. stackoverflow.com/a/30178723/1212596
  2. 11

    Me encontré con este problema un par de veces, y de hecho es un poco molesto, pero no demasiado. Los miembros abstractos y los parámetros son por lo general dos maneras de hacer la misma cosa, con sus ventajas y desventajas; por rasgos tener un miembro abstracto no es demasiado inconveniente, porque se necesita de todos modos otra clase para implementar la característica.*

    Por lo tanto, usted simplemente debe tener un resumen de la declaración del valor en el rasgo, por lo que la implementación de clases para el suministro de un implícito para usted. Vea el siguiente ejemplo, que compila correctamente, y muestra dos formas de implementar el dado rasgo:

    trait Base[T] {
        val numT: Ordering[T]
    }
    /* Here we use a context bound, thus cannot specify the name of the implicit
     * and must define the field explicitly.
     */
    class Der1[T: Ordering] extends Base[T] {
        val numT = implicitly[Ordering[T]]
        //Type inference cannot figure out the type parameter of implicitly in the previous line
    }
    /* Here we specify an implicit parameter, but add val, so that it automatically
     * implements the abstract value of the superclass.
     */
    class Der2[T](implicit val numT: Ordering[T]) extends Base[T]

    La idea básica de que muestre también está presente en el Knut Arne Vedaa la respuesta, pero he tratado de hacer más atractiva y conveniente ejemplo, eliminar el uso de las características.

    *Esta no es la razón por la que el rasgo no puede aceptar parámetros – no lo sé. Sólo estoy argumentando que la limitación es aceptable en este caso.

    • Sin embargo, de esta manera usted no tiene acceso a una implícita Ordering[T] mientras que la definición de los métodos en Base[T]. Y si usted hace numT implícita, a solucionar esto problema, pero val numT = implicitly[Ordering[T]] convierte en un bucle infinito.
    • Si modifica Base para abordar este problema, usted no puede escribir Der1, pero Der2 obras de todos modos – y es aún más compacto que el de Der1 mientras equivalentes. trait Base[T] { implicit val numT: Ordering[T] } class Der2[T](implicit val numT: Ordering[T]) extends Base[T]
  3. 11

    Esto no es posible.

    Pero puede utilizar implicitly y Scala inferencia de tipos para hacer esto tan sin dolor como sea posible.

    trait MyTrait {
    
        protected[this] implicit def e: ClassName
    
    }

    y, a continuación,

    class MyClass extends MyTrait {
    
        protected[this] val e = implicitly //or def
    
    }

    Sucinta, y no es necesario escribir el tipo en la ampliación de la clase.

  4. 0

    Que usted podría hacer algo como esto:

    abstract class C
    
    trait A { this: C =>
        val i: Int
    }    
    
    implicit val n = 3
    
    val a = new C with A {
        val i = implicitly[Int]
    }

    Pero no estoy seguro de si hay un punto en que – que bien podría hacer referencia al valor implícito de forma explícita.

    Supongo que lo que quiere es deshacerse de la aplicación de i en la creación de la instancia, pero como dices a ti mismo, el núcleo del problema es que los rasgos no toma parámetros del constructor – ya sean implícitos o no no importa.

    Una posible solución para este problema sería añadir una nueva característica a la que ya sintaxis válida:

    trait A {
        implicit val i: Int
    }

    donde i sería ejecutado por el compilador si un implícito fue en el ámbito.

  5. 0

    Como parece que esto no es posible, yo fui por la opción de la declaración implícita de val en la clase base’ constructor. Como se señaló en la pregunta que esto no es lo ideal, pero satisface el compilador y, de manera pragmática, no es demasiado de una carga en mi caso en particular.

    Si alguien tiene una mejor solución, aunque, yo estaría feliz de escuchar y aceptar.

Dejar respuesta

Please enter your comment!
Please enter your name here