objetos metodos introduccion funciones ejemplos clases scala types compilation type-inference type-systems

metodos - Scala Puzzle: imponiendo que dos argumentos de función son del mismo tipo Y ambos son un subtipo de una clase dada



objetos en scala (4)

¿Cómo puedo hacer que los argumentos de trickyMethod sean los mismos en el momento de la compilación, pero al mismo tiempo también tienen el tipo común de Fruit ?

En otras palabras, tricky.trickyMethod(new Banana,new Apple) no debería compilarse.

Estoy seguro de que debe haber una solución simple, pero acabo de pasar una hora buscando la respuesta y todavía no tengo ni idea :(

Intenté evidencia implícita con <: <pero no pude hacer que funcionara.

class Fruit class Apple extends Fruit class Banana extends Fruit class TrickyClass[T<:Fruit]{ def trickyMethod(p1:T,p2:T)= println("I am tricky to solve!") } object TypeInferenceQuestion extends App{ val tricky=new TrickyClass[Fruit]() tricky.trickyMethod(new Apple,new Apple) //this should be OK tricky.trickyMethod(new Banana,new Banana) //this should be OK tricky.trickyMethod(new Banana,new Apple) //this should NOT compile }

EDITAR:

Gracias por las respuestas !

Pregunta de seguimiento (más general):

Este segundo ejemplo es un caso más general del primer ejemplo.

class Fruit class Apple extends Fruit class Banana extends Fruit class TrickyClass[T]{ def trickyMethod[S<:T](p1:S,p2:S)= println("I am tricky to solve!") } object TypeInferenceQuestion extends App{ val tricky=new TrickyClass[Fruit]() tricky.trickyMethod(new Apple,new Apple) //this should be OK tricky.trickyMethod(new Banana,new Banana) //this should be OK tricky.trickyMethod(new Banana,new Apple) //this should NOT compile }


Podrías hacerlo :

class Tricky[T] { def trickyMethod[S1<:T,S2<:T](s1:S1,s2:S2)(implicit ev: S1=:=S2) = println() } scala> val t = new Tricky[Seq[Int]] t: Tricky[Seq[Int]] = Tricky@2e585191 scala> t.trickyMethod(List(1),List(1)) //OK scala> t.trickyMethod(List(1),Seq(1)) <console>:10: error: Cannot prove that List[Int] =:= Seq[Int]. t.trickyMethod(List(1),Seq(1))


Pruebe algo como esto:

abstract class Fruit[T <: Fruit[T]] { def trick(that: T) } class Apple extends Fruit[Apple] { override def trick(that: Apple) = { println("Apple") } } class Banana extends Fruit[Banana] { override def trick(that: Banana) = { println("Banana") } } class TrickyClass{ def trickyMethod[T<:Fruit[T]](p1:T,p2:T)= p1.trick(p2) } object TypeInferenceQuestion { val tricky=new TrickyClass() tricky.trickyMethod(new Apple,new Apple) //this should be OK tricky.trickyMethod(new Banana,new Banana) //this should be OK tricky.trickyMethod(new Banana,new Apple) //this should NOT compile }

da:

error: inferred type arguments [Fruit[_ >: Apple with Banana <: Fruit[_ >: Apple with Banana <: ScalaObject]]] do not conform to method trickyMethod''s type parameter bounds [T <: Fruit[T]] tricky.trickyMethod(new Banana,new Apple) //this should NOT compile

En el último.


Puede usar implícitos, aunque deberá definir uno para cada subclase de Fruit :

class Fruit class Apple extends Fruit class Banana extends Fruit trait FruitEvidence[T <: Fruit] class TrickyClass{ def trickyMethod[T <: Fruit](p1:T,p2:T)(implicit e: FruitEvidence[T]) = println("I am tricky to solve!") } object FruitImplicits { implicit val BananaEvidence = new FruitEvidence[Banana] { } implicit val AppleEvidence = new FruitEvidence[Apple] { } } object TypeInferenceQuestion extends App{ import FruitImplicits._ val tricky=new TrickyClass() tricky.trickyMethod(new Apple,new Apple) //this should be OK tricky.trickyMethod(new Banana,new Banana) //this should be OK tricky.trickyMethod(new Banana,new Apple) //this should NOT compile }


Puedes hacer esto con implicits como ese. Tenga en cuenta que siempre podrá

tricky.trickyMethod((new Banana): Fruit, (new Apple): Fruit)

ya que no permitir eso rompería la relación de subtipado. (Si es realmente necesario, puede usar la codificación de Miles Sabin de "no este tipo" (ver comentario # 38) para rechazar Fruit .)

De todos modos, una forma de lograr lo que quieres es la siguiente:

class SamePair[T,U] {} object SamePair extends SamePair[Nothing,Nothing] { def as[A] = this.asInstanceOf[SamePair[A,A]] } implicit def weAreTheSame[A] = SamePair.as[A]

Ahora tenemos un implícito que nos dará un objeto ficticio que verifica que dos tipos son iguales.

Así que ahora cambiamos TrickyClass a

class TrickyClass[T <: Fruit] { def trickyMethod[A <: T, B <: T](p1: A, p2: B)(implicit same: SamePair[A,B]) = println("!") }

Y hace lo que quieres:

scala> val tricky=new TrickyClass[Fruit]() tricky: TrickyClass[Fruit] = TrickyClass@1483ce25 scala> tricky.trickyMethod(new Apple,new Apple) //this should be OK ! scala> tricky.trickyMethod(new Banana,new Banana) //this should be OK ! scala> tricky.trickyMethod(new Banana,new Apple) //this should NOT compile <console>:16: error: could not find implicit value for parameter same: SamePair[Banana,Apple] tricky.trickyMethod(new Banana,new Apple) //this should NOT compile