2014-08-28 6 views
8

Perché questo non viene compilato?
L'errore dato è class SomeElement needs to be abstract, since method eval in trait Element of type [T <: Typed]=> scala.util.Try[T] is not definedPerché scalac non crede che un metodo non si adatti alla firma del tipo richiesto?

non riesco a capire il motivo per cui il metodo eval definita sulla SomeElement non soddisfa i vincoli di tipo.

Come ho capito, eval dovrebbe restituire qualcosa racchiuso in un Try che sottoclasse Typed. La prova è covariante nel suo parametro tipo. L'implementazione di eval in SomeElement restituisce una sottoclasse NumberLike sottoclasse Typed. Allora, cosa c'è che non va?

import scala.util.Try 

trait Element { 
    def eval[T <: Typed]: Try[T] 
} 

trait Typed 

case class NumberLike(n: Long) extends Typed 

case class SomeElement(n: Long) extends Element { 
    def eval = Try { 
    NumberLike(n) 
    } 
} 

object app extends Application { 
    println (SomeElement(5).eval) 
} 

Cercando di aggiungere un parametro di tipo esplicito a eval in SomeElement non aiuta:

case class SomeElement(n: Long) extends Element { 
    def eval[NumberLike] = Try { 
    NumberLike(n) 
    } 
} 

Modifica alla definizione di SomeElement a quanto sopra dà:

found : <empty>.NumberLike 
    required: NumberLike(in method eval) 
    NumberLike(n) 

EDIT Mi piacerebbe davvero sapere perché non viene compilato. Soluzioni alternative per il problema sono utili, ma voglio davvero sapere cosa sta succedendo qui.

+1

non posso scrivi una risposta completa ora, ma sicuramente, non puoi eseguire l'override/implementare la funzione poly con il parametro type by function senza di essa; quindi devi scrivere smth come 'def eval [T: NumberLike] = ...' – DaunnC

+0

Perché 'tratto astratto '? – cchantep

+0

@DaunnC vedi modifica – Squidly

risposta

2

Il tipo di parametro T essendo definita sulla funzione, non sul tipo contenitore Element, non può essere 'cancellato' per successione e deve essere mantenuta la funzione prevalente (http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#class-members).

Spostando il parametro di tipo su Element definizione, farlo funzionare come segue.

trait Element[T <: Typed] { 
    def eval: Try[T] 
} 

trait Typed 

case class NumberLike(n: Long) extends Typed 

case class SomeElement(n: Long) extends Element[NumberLike] { 
    def eval = Try { 
    NumberLike(n) 
    } 
} 

o utilizzando un membro di tipo:

trait Element { 
    type T <: Typed 
    def eval: Try[T] 
} 

trait Typed 

case class SomeElement(n: Long) extends Element { 
    type T = NumberLike 
    def eval = Try { 
    NumberLike(n) 
    } 
} 
+0

Ciò significherebbe che tutte le cose che estendono Element devono fornire un parametro di tipo per Element. – Squidly

+1

O quello, o usando un membro del tipo (vedi modifica) – cchantep

+0

Preferisco il 2o. Penso che dovrebbe essere pronto 'T <: Typed' però. – Squidly

0

Poiché qualcuno ha dato una soluzione, che sto cercando di spiegare perché.

in Java, qualcosa di simile è illegale:

class A { 
    <T> T f() {return null;} 
    Object f() {return null;} 
} 

Ma in scala, è legale:

class A { 
    def f[T](): T = ??? 
    def f(): Any = ??? 
} 

La differenza principale è che: Scala trattare 2 metodi con lo stesso nome e parametri lista diverso se uno di loro ha un parametro di tipo e un altro no (credo che la firma del metodo scala includa parametri di tipo, correggimi se ho sbagliato :))

Quindi nel tuo caso, Non è possibile sovrascrivere un metodo con il parametro di tipo con un metodo normale. (hanno diverse firme)

io non sono sicuro che uno è migliore, personalmente preferisco la regola Scala :)

Problemi correlati