/ / Scala: tipi astratti e mixin - scala, types, abstract

Scala: Tipi astratti e Mixin - scala, tipi, astratti

Supponiamo di avere due classi con lo stesso identificatore per un tipo parametrizzato

  trait A {
type O
def f(a: Int, os: Seq[O]): Int
}

trait B {
type O
def g(a: Int, o: O): Int = { h1(o) + h2(a) }
def h1(o: O): Int
def h2(a: Int): Int = {a/2}
}

Vorrei creare una classe bambino che "sposerà" i due

  trait C extends A with B {
def f(a: Int, os: Seq[O]): Int = {
os.map{ o => g(a, o) }.sum
}
}

Infine, creo un'implementazione per C

  class D extends C {
type O = Int
def h1(o: O): Int = {5 * o}
}

Al momento della scrittura di C Non so ancora di che tipo O è - tuttavia, mi piacerebbe limitare A.O == B.O in modo tale che "abbia senso" da usare B.g in un'implementazione di A.f. Ho provato a implementarlo e sorprendentemente sembrava che Scala pensasse che ce ne fosse solo uno type O

  val d = new D
println(d.f(1, Seq(1,2,3)))

A me questo sembra sbagliato, perché dovrebbe A.O e B.O essere d'accordo?

MODIFICARE Vorrei anche notare che se invece dovessi porre dei vincoli su O in questo modo,

  case class Z()
case class Z1() extends Z
case class Z2() extends Z1

trait A {
type O <: Z
}

trait B {
type O >: Z2
}


class D extends C {
type O = Z1

La compilazione fallirà. Tuttavia, se metti questo invece,

  trait A {
type O <: Z
}

trait B {
type O <: Z1
}
class D extends C {
type O = Z2

La compilazione riesce e tutto funziona correttamente.

risposte:

4 per risposta № 1

Penso che Scala "sposa" sempre i membri - sia membri del tipo che membri del valore - dei tratti quando entrambi sono mescolati in:

trait A { type T = Int }
trait B { type T = String }

object C extends A with B

sovrascrivendo il tipo T nel tratto A, che è uguale a Int; digitare T nel tratto B, che è uguale a String, necessita del modificatore ʻoverride "

(è così che Scala affronta il problema dell'ereditarietà multipla - non importa quante volte un identificatore viene mischiato, esiste solo una volta).


2 per risposta № 2

Il secondo esempio fallisce perché Scala ha bisogno di un po 'di aiuto per stabilire i limiti di tipo in C. Suppongo che forse dovrebbe essere abbastanza intelligente da capirlo da solo, ma qualcuno più esperto in teoria dei tipi dovrebbe spiegare perché o perché no. Questa definizione di C dovrebbe funzionare:

trait C extends A with B {
type O >: Z2 <: Z // restrict O"s type so it"s valid for both A and B
}

La gestione di Scala dei tipi astratti è simile ala sua gestione di metodi astratti. I metodi con lo stesso nome (e firma) estratti da più tratti vengono sovrascritti insieme. Prendi il classico controesempio, derivato da un esempio fornito in Ye Olde C ++ Programming Language.

trait Cowboy {
/** Shoot something */
def draw
}

trait Window {
/** Draw something */
def draw
}

class CowboyWindow {
/** Probably best to draw something on the screen, then shoot it */
def draw {
// ...
}
}

Allo stesso modo in cui si assume che ce ne sia solo uno type O nel codice, si presume che ci sia un solo metodo draw in questo codice. In questo caso, questa ipotesi è problematica, poiché potresti finire per spararti ai piedi.

Come hai scoperto nel tuo esempio, di solito questa ipotesi funziona per il meglio (pensa a più interfacce che dichiarano un file close() metodo). Ma se questa ipotesi è problematica per qualche classe, come lo è per CowboyWindow, puoi sostituire l'ereditarietà con la composizione per aggirarla.