2015-04-27 16 views
5

Sto leggendo la programmazione funzionale in Scala e nel capitolo 04 gli autori implementano l'opzione da soli. Ora, quando si definisce la funzione getOrElse che utilizzano un limite superiore per limitare il tipo di A ad un supertipo (se inteso correttamente)Scala Tipo di limite superiore non compreso

Così, la definizione va:

sealed trait Option[+A] { 
    def getOrElse[B >: A](default: => B): B = this match { 
    case None => default 
    case Some(a) => a 
    } 
} 

Così, quando abbiamo qualcosa come

val a = Some(4) 
println(a.getOrElse(None)) => println prints a integer value 
val b = None 
println(b.getOrElse(Some(3)) => println prints a Option[Integer] value 

a ha tipo Option[Int], così A sarebbe tipo Int. B sarebbe il tipo Nothing. Nothing è un sottotipo di ogni altro tipo. Ciò significa che Option[Nothing] è un sottotipo di Option[Int] (a causa della covarianza), giusto?

Ma con B >: A abbiamo detto che B deve essere un supertipo ?! Quindi, come possiamo ottenere un Int indietro? Questo è un po 'di confusione per me ...

Qualcuno dovrebbe cercare di chiarire?

risposta

8

Ciò significa che l'opzione [Nothing] è un sottotipo di Option [Int] (a causa della covarianza), giusto?

Corretto. Option[Nothing] è un Option[Int].

Ma con B>: A abbiamo detto che B deve essere un supertipo ?! Quindi, come possiamo ottenere un back int?

Non deve essere un super-tipo. Richiede solo A come con limite inferiore. Il che significa che puoi ancora passare Int a getOrElse se A è Int.

Ma ciò non significa che non è possibile passare le istanze di una sottoclasse. Per esempio:

class A 
class B extends A 
class C extends B 

scala> Option(new B) 
res196: Option[B] = Some([email protected]) 

scala> res196.getOrElse(new C) 
res197: B = [email protected] 

scala> res196.getOrElse(new A) 
res198: A = [email protected] 

scala> res196.getOrElse("...") 
res199: Object = [email protected] 

posso ancora passare un'istanza di C, perché C può essere up-cast B. Posso anche passare un tipo più in alto nell'albero dell'ereditarietà, e invece getOrElse restituirà quel tipo. Se passo un tipo che non ha nulla a che fare con il tipo contenuto nello Option, verrà inferito il tipo con il limite superiore. Nel caso precedente, è Any.


Quindi perché il limite inferiore esiste? Perché non ha:

def getOrElse[B <: A](default: => B): B 

Questo non funzionerà perché getOrElse deve o restituire il A che è contenuto nel Option, o il default B. Ma se restituiamo il A e A non è un B, quindi il tipo non è valido.Forse, se getOrElse restituito A:

def getOrElse[B <: A](default: => B): A 

Questo lavoro (se fosse davvero definito in questo modo), ma si sarebbe limitato dal tipo-limiti. Quindi, nel mio esempio precedente, è possibile passare solo B o C a getOrElse su un Option[B]. In ogni caso, questo non è come è nella libreria standard.


La libreria standard getOrElse consente di passare qualsiasi cosa ad esso. Supponi di avere Option[A]. Se passiamo un sottotipo di A, viene trasmesso a A. Se passiamo a A, ovviamente questo va bene. E se passiamo qualche altro tipo, il compilatore deduce il limite superiore tra i due. In tutti i casi, il limite di tipo B >: A è soddisfatto.

Perché getOrElse consente di passare qualsiasi cosa, molti lo considerano molto complicato. Ad esempio si potrebbe avere:

val number = "blah" 
// ... lots of code 
val result = Option(1).getOrElse(number) 

E questo verrà compilato. Avremo solo uno Option[Any] che probabilmente causerà un errore da qualche parte lungo la linea.

+0

Grazie, ma potresti fornire un esempio di quale tipo non saremmo in grado di passare a res196.getOrElse ... al momento, con questo esempio, non vedo il punto di avere un limite inferiore. O meglio per dire, ora non capisco cosa significhi che un tipo sia un limite inferiore ... – Marin

+0

@Marin Puoi passare qualsiasi tipo a 'getOrElse'. Vedi la mia modifica. –

+0

grazie! Penso di averlo capito ora. – Marin

Problemi correlati