2012-05-24 15 views
18

Ho osservato una differenza nell'inferenza di tipo Scala quando applicata a def e val.Inferenza di tipo diverso per `def` e` val` in Scala

Utilizzando def, è possibile definire un metodo nullario astratto const restituendo un valore di tipo Int => Int. Quando si implementa const con una funzione letterale, non devono fornire un tipo di parametro, come si può dedurre dal compilatore:

trait D { 
    def const: Int => Int 
} 
object D extends D { 
    def const = i => i + 1 
} 

Questo va bene. (Il lato negativo, viene creata una nuova istanza funzione per ogni accesso al D.const.)

consideri ora una costruzione analoga utilizzando val:

trait V { 
    val const: Int => Int 
} 
object V extends V { 
    val const = i => i + 1 
} 

Questo non verrà compilato, avendo con

error: missing parameter type 
    val const = i => i + 1 
      ^

Perché?

+1

Non è davvero un'idea sicura utilizzare i 'val' astratti in caratteri (può portare a NPE sorprendenti). Mantieni un 'def' nel tratto e sovrascrivilo con un' val' nell'implementazione. Oppure inizia con 'lazy val x: X = sys.error (" override me ")' nel tratto. – ron

+0

Potrebbe avere qualcosa a che fare con la corrispondenza dei pattern? Ogni cosa tra il segno 'def' e il segno uguale' = 'viene trattato come un identificatore, mentre tutto tra' val' e il segno uguale '=' viene trattato come un modello. Solo un'ipotesi ... – agilesteel

+0

@ron, grazie per averlo menzionato, ma in un modo o nell'altro la domanda rimane, poiché l'uso di un 'def' nel tratto e un' val' nell'implementazione porta allo stesso problema. – knuton

risposta

1

Come di Scala 2.9.1, questo è “come speced”. Citando Martin Odersky dal SI-2742:

Per i metodi, i tipi di restituzione nei metodi astratti ereditati vengono presi come previsto sul lato destro. Per i valori non esiste una regola del genere. Quindi questa sarebbe una richiesta di miglioramento delle specifiche, come la vedo io.

Il ticket ha priorità bassa ed è rimasto invariato dalla sua prima discussione a fine 2009, quindi sembra improbabile che cambi presto.

2

Se si crea questo codice con l'opzione -Xprint tutto, si vedrà che:

abstract trait V extends scala.AnyRef { 
<stable> <accessor> def const: Int => Int 
}; 

final object V extends java.lang.Object with V with ScalaObject { 

def this(): object V = { 
    V.super.this(); 
() 
}; 

private[this] val const: <error> => <error> = ((i: <error>) => i.+(1)); 
<stable> <accessor> def const: <error> => <error> = V.this.const 
} 

Quindi l'errore si verifica al momento della creazione della val privato e di accesso. Il compilatore tenta di valutare il valore interessato alla val const prima di creare l'accessor def const.

se si guarda al valore val const definito in tratto, si vede che la creazione di VAL privata è stata disabilitata perché è solo una definizione per l'accesso def cost.

Penso che il tipo di inferenza con definizione precedente (in tratto o superclasse) si sia verificato solo quando ha tentato di creare l'accessor, non per valutare un valore.

E per l'ultima const DEF, il tipo si basa solo sul privato [questo] val tipo const: error => errore

+0

Sì , deve essere così! Puoi giudicare se questo è secondo le specifiche o semplicemente un bug nel compilatore? – knuton

+0

Non sto considerando che come un bug, penso, in questo caso, non hai ragione di usare un val perché il valore è definito solo al momento della compilazione, non al momento dell'esecuzione (non c'è alcun riferimento nel valore a un parametro o una variabile precedentemente definiti, è solo una definizione di una funzione anonima, ma potrebbe essere Martin Odersky vedrà questo post e ci darà più spiegazioni;) – fp4me

+0

Beh, questo è solo un esempio di giocattolo. Nel codice da cui l'ho derivato, il corpo della funzione anonima usa un nome dall'ambito che lo racchiude. Penso che il fatto che questa istanza specifica possa essere risolta in modo diverso, non lasci il compilatore in difficoltà. – knuton

Problemi correlati