2010-09-28 11 views
9

Considere el siguiente código:resolución parámetro implícito para los tipos más altos kinded

object foo { 

    trait Bar[Q[_]] 

    implicit object OptionBar extends Bar[Option] 

    def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) =() 

    def main(args: Array[String]) { 
     test(Some(42): Option[Int]) //??? 
    } 
} 

Esto funciona, pero necesita escribir el Algunos (42) como opción [Int], de lo contrario el objeto implícito OptionBar no será resuelto (porque en su lugar se espera una barra [Algunos]). ¿Hay alguna manera de evitar el tipado explícito, de forma que obtengo el objeto OptionBar implícito en prueba incluso si realizo la prueba con Some o None?

[Aclaración]

  • que usa la opción aquí sólo como ejemplo, también debería funcionar si tengo un Bar para una clase abstracta etc.
  • La solución también debe trabajar cuando otros no relacionados Bares, son en su alcance, decir implicit object listBar extends Bar[list]

[actualización]

parece que hacer contravariant parámetro de barra hace t que truco:

object foo { 

    trait Bar[-Q[_]] //<--------------- 

    implicit object OptionBar extends Bar[Option] 
    implicit object ListBar extends Bar[List] 

    def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) =() 

    def main(args:Array[String]) { 
    test(Some(42)) 
    } 
} 

Pero por supuesto, esto es una grave limitación de las posibilidades de bar, así que todavía hay esperanza para una mejor respuesta.

+1

¿Por qué la contravarianza es una limitación grave? Sin usarlo, Bar es invariante. Si intentas usar Bar como una clase de tipo contra tipos de alto nivel, la contravariancia parece encajar en mi mente. Eso es, por supuesto, hasta que quieras tratar las subclases de manera diferente. Sin embargo, en ese caso, todavía tiene otros trucos, como "prioridades" de resolución implícita – jsuereth

+0

@Josh: considere algo así como 'barra de rasgo [Q [_]] {def cero [T]: Q [T]}', sin ajustar Ninguna y Nil en mis ejemplos. Pero no puedo tener ese método en Bar, si defino Q como contravariante. Cuando sepa cómo resolver esto, hágamelo saber ... – Landei

+1

Además, para una clase de tipo naturalmente contravariante, como 'Igual [T]', la búsqueda implícita favorecerá 'Igual [Animal]' sobre 'Igual [Perro] ': http://www.scala-lang.org/node/4626. Las clases de herencia y tipo son realmente difíciles de combinar. – retronym

Respuesta

7

No va a trabajar en todos los casos, pero como se ha dicho, puede intentar esto:

object foo { 
    trait Bar[Q[_]] 

    implicit object OptionBar extends Bar[Option] 

    def test[T, C[_], D](c: D)(implicit bar: Bar[C], ev: D <:< C[T]) =() 

    def main(args: Array[String]) { 
    test(Some(42)) //??? 
    } 
} 

Curiosamente, esto no supone, a pesar de que expresa la misma cosa:

def test[T, C[_], D <: C[T]](c: D)(implicit bar: Bar[C]) =() 

Para obtener más información sobre <:<, consulte:

+0

Esto funciona, sin embargo, si agrega otro implícito para Bar en ese ámbito, digamos 'el objeto implícito ListBar extends Bar [List]' se obtiene un error de "valores implícitos ambiguos". – Landei

+0

Doy la recompensa a esta respuesta, porque muestra una buena técnica y resuelve la pregunta inicial, que no fue formulada con la precisión suficiente. Sin embargo, aún aprecio obtener una pista sobre cómo resolver el problema de los "valores implícitos ambiguos". – Landei

+0

Bueno, tres años y varias versiones de Scala más adelante, no parece que exista una respuesta. (Estoy enfrentando el mismo desafío.) – Tim

5

Eso es porque Some(42) es un tipo más específico que Option[Int]. Es un Some[Int]. Vea la codificación alternativa a continuación:

object foo { 

    trait Bar[Q[_]] 

    implicit object OptionBar extends Bar[Option] 

    def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) =() 

    def main(args: Array[String]) { 
     test(Option(42)) 
    } 
} 
+1

No defiendo realmente el uso de 'Option.apply' a menos que se use para codificar una verificación nula. A propósito, con scalaz, escribiría '42.some' o' 42.pure [Option] ', ambos de los cuales se escriben como' Opción [Int] '. – retronym

+0

@retronym - ¿Por qué no abogas por usar 'Option.apply'? –

+1

Lo uso cuando tengo una referencia potencialmente anulable que quiere convertir a 'Some' o a' None'. Puedo leer 'Some (x): Option [A]' y razonar que tendré un 'Some' sin rastrear el origen de' x'. Si el argumento es literal, como en este ejemplo, está bien. – retronym

Cuestiones relacionadas