In che modo i tratti di Scala evitano l'errore "diamante"?

16

(Nota: ho usato "error" invece di "problem" nel titolo per ovvi motivi ..;)).

Ho fatto alcune letture di base sui Tratti in Scala. Sono simili alle interfacce in Java o C #, ma consentono l'implementazione predefinita di un metodo.

Mi stavo chiedendo: questo non può causare un caso di "problema dei diamanti", motivo per cui molte lingue evitano l'ereditarietà multipla in primo luogo?

In caso affermativo, come fa Scala a gestirlo?

    
posta Aviv Cohn 26.04.2014 - 21:01
fonte

2 risposte

21

Il problema dei diamanti è l'incapacità di decidere quale implementazione del metodo scegliere. Scala risolve questo definendo quale implementazione scegliere come parte delle specifiche della lingua ( leggi la parte su Scala in questo articolo di Wikipedia ).

Naturalmente, la definizione dello stesso ordine potrebbe anche essere utilizzata in ereditarietà multipla di classe, quindi perché preoccuparsi dei tratti?

La ragione per cui IMO è costruttori. I costruttori hanno diverse limitazioni che i metodi normali non hanno - possono essere chiamati solo una volta per oggetto, devono essere chiamati per ogni nuovo oggetto, e un costruttore di classi child deve chiamarlo come costruttore del genitore come prima istruzione (la maggior parte delle lingue fallo implicitamente per te se non hai bisogno di passare i parametri).

Se B e C ereditano A e D ereditano B e C, ed entrambi i costruttori di B e C chiamano il costruttore di A, allora il costruttore di D's chiamerà il costruttore di A due volte. Definire quali implementazioni scegliere come Scala ha fatto con i metodi non funzionerà qui perché entrambi i costruttori di B e C devono essere chiamati.

I tratti evitano questo problema poiché non hanno costruttori.

    
risposta data 26.04.2014 - 22:04
fonte
17

Scala evita il problema dei diamanti con qualcosa chiamato "linearizzazione dei tratti". Fondamentalmente, cerca l'implementazione del metodo nei tratti che estendi da destra a sinistra. Semplice esempio:

trait Base {
   def op: String
}

trait Foo extends Base {
   override def op = "foo"
}

trait Bar extends Base {
   override def op = "bar"
}

class A extends Foo with Bar
class B extends Bar with Foo

(new A).op
// res0: String = bar

(new B).op
// res1: String = foo

Detto questo, l'elenco delle caratteristiche che cerca potrebbe contenere più di quelle che hai dato esplicitamente, dal momento che potrebbero estendere altri tratti. Una spiegazione dettagliata è fornita qui: Tratti come modifiche impilabili e un esempio più completo della linearizzazione qui: Perché non ereditarietà multipla?

Credo che in altri linguaggi di programmazione questo comportamento venga talvolta definito come "Ordine di risoluzione del metodo" o "MRO".

    
risposta data 14.05.2014 - 11:25
fonte

Leggi altre domande sui tag