def conversion scala types implicit

conversion - scala annotations



que es diferente entre<:<y<: en scala (4)

Definitivamente hay diferencias entre <: y <:< ; aquí está mi intento de explicar cuál elegir.

Tomemos dos clases:

trait U class V extends U

  • La restricción de tipo <: siempre se usa porque dirige la inferencia de tipo. Eso es lo único que puede hacer: restringir el tipo en su lado izquierdo.

    El tipo restringido debe referenciarse en algún lugar, generalmente en la lista de parámetros (o tipo de retorno), como en:

    def whatever[A <: U](p: A): List[A] = ???

    De esta forma, el compilador lanzará un error si la entrada no es una subclase de U , y al mismo tiempo le permitirá referirse al tipo de entrada por nombre para su uso posterior (por ejemplo, en el tipo de devolución). Tenga en cuenta que si no tiene ese segundo requisito, todo esto no es necesario (con excepciones ...), como en:

    def whatever(p: U): String = ??? // this will obviously only accept T <: U

  • La Restricción de Tipo Generalizada <:< por otro lado, tiene dos usos:

    1. Puede usarlo como una prueba posterior al hecho de que se dedujo algún tipo. Como en:

      class List[+A] { def sum(implicit ev: A =:= Int) = ??? }

      Puede crear dicha lista de cualquier tipo, pero solo se puede llamar a la sum cuando tiene la prueba de que A es realmente Int .

    2. Puede usar la ''prueba'' anterior como una forma de inferir aún más tipos. Esto le permite inferir tipos en dos pasos en lugar de uno.

      Por ejemplo, en la clase de List anterior, puede agregar un método de flatten :

      def flatten[B](implicit ev: A <:< List[B]): List[B]

      Esto no es solo una prueba, esta es una manera de agarrar ese tipo interno B con A ahora arreglado .

      Esto también se puede usar con el mismo método: imagine que desea escribir una función de sort utilidad, y desea tanto el tipo de elemento T como el tipo de colección Coll . Puede tener la tentación de escribir lo siguiente:

      def sort[T, Coll <: Seq[T]](l: Coll): Coll

      Pero T no está obligado a contener nada: no aparece en los argumentos ni en el tipo de salida. Así que T terminará como Nothing , o Any , o lo que quiera que el compilador quiera, en realidad (normalmente Nothing ). Pero con esta versión:

      def sort[T, Coll](l: Coll)(implicit ev: Coll <:< Seq[T]): Coll

      Ahora T aparece en los tipos de parámetros. Habrá dos ejecuciones de inferencia (una por lista de parámetros): Coll se deducirá de lo que se haya dado, y luego, más adelante, se buscará un implícito, y si se encuentra, T se deducirá con Coll ahora corregido . Esto esencialmente extrae el parámetro de tipo T del Coll deducido previamente.

Así que esencialmente, <:< comprueba (y potencialmente infiere) los tipos como un efecto secundario de la resolución implícita, por lo que se puede utilizar en diferentes lugares / en momentos diferentes que la inferencia de tipo de parámetro. Cuando hagan lo mismo, quédate con <:

Eso ya lo se:

  • <: es la restricción de tipo de sintaxis de Scala
  • mientras <:< es el tipo que aprovecha el Scala implícito para alcanzar el tipo de restricción

por ejemplo:

object Test { // the function foo and bar can have the same effect def foo[A](i:A)(implicit ev : A <:< java.io.Serializable) = i foo(1) // compile error foo("hi") def bar[A <: java.io.Serializable](i:A) = i bar(1) // compile error bar("hi") }

pero quiero saber cuándo necesitamos usar <: y <:< ?

y si ya tenemos <: ¿por qué necesitamos <:< ?

¡Gracias!


Después de pensar un poco, creo que tiene algo diferente. por ejemplo:

object TestAgain { class Test[A](a: A) { def foo[A <: AnyRef] = a def bar(implicit ev: A <:< AnyRef) = a } val test = new Test(1) test.foo // return 1 test.bar // error: Cannot prove that Int <:< AnyRef. }

este menas:

  • el alcance de <: está solo en el método param genérico tpye scope foo[A <: AnyRef] . En el ejemplo, el método foo tiene su tpye genérico A , pero no el A en clase Test[A]
  • el alcance de <:< , primero encontrará el tipo genérico del método, pero la bar método no tendrá el tipo genérico param, por lo que encontrará el tipo genérico de Test[A] .

entonces, creo que es la principal diferencia.


La diferencia principal entre los dos es que <: es una restricción en el tipo, mientras que <:< es un tipo para el cual el compilador tiene que encontrar evidencia, cuando se usa como un parámetro implícito. Lo que eso significa para nuestro programa es que en el <: caso, el tipo inferencer intentará encontrar un tipo que satisfaga esta restricción. P.ej

def foo[A, B <: A](a: A, b: B) = (a,b) scala> foo(1, List(1,2,3)) res1: (Any, List[Int]) = (1,List(1, 2, 3))

Aquí el inferencer encuentra que Int y List[Int] tienen el tipo super común Any , por lo que infiere que para A satisface B <: A

<:< es más restrictivo, porque el tipo inferencer se ejecuta antes que la resolución implícita. Entonces los tipos ya están corregidos cuando el compilador intenta encontrar la evidencia. P.ej

def bar[A,B](a: A, b: B)(implicit ev: B <:< A) = (a,b) scala> bar(1,1) res2: (Int, Int) = (1,1) scala> bar(1,List(1,2,3)) <console>:9: error: Cannot prove that List[Int] <:< Int. bar(1,List(1,2,3)) ^


1. def bar[A <: java.io.Serializable](i:A) = i

<: - garantiza que la instancia de i del tipo parámetro A será subtipo de Serializable

2. def foo[A](i:A)(implicit ev : A <:< java.io.Serializable) = i

<: <- garantiza que el contexto de ejecución contendrá un valor implícito (para ev paramenter) de tipo A, que es el subtipo de Serializable . Esto implícito se define en Predef.scala y para el método foo y se comprueba si la instancia del tipo parámetro A es subtipo de Serializable :

implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

caso ficticio de usar el operador <: <:

class Boo[A](x: A) { def get: A = x def div(implicit ev : A <:< Double) = x / 2 def inc(implicit ev : A <:< Int) = x + 1 } val a = new Boo("hi") a.get // - OK a.div // - compile time error String not subtype of Double a.inc // - compile tile error String not subtype of Int val b = new Boo(10.0) b.get // - OK b.div // - OK b.inc // - compile time error Double not subtype of Int val c = new Boo(10) c.get // - OK c.div // - compile time error Int not subtype of Double c.inc // - OK

  • si no llamamos a los métodos qué no cumple <: <condición que todos compilan y ejecutan.