2015-07-13 10 views
13

Vorrei passare un oggetto a una funzione che accetta un argomento con un tipo proiettato, e ottenere Scala per dedurre che il tipo dell'oggetto proviene dall'oggetto che lo racchiude. Ecco alcune semplici righe di codice per illustrare la difficoltà:Perché Scala non può inferire il percorso di un tipo dipendente dal percorso, anche da un riferimento automatico esplicito?

trait Cult { cult_ => 
    case class CultLeader(personality: Personality) { 
    val cult = cult_ 
    val follower = personality.attractFollower(this) 
    } 
    case class Follower(leader: CultLeader, name: String) 
} 

trait Personality { 
    def attractFollower(leader: Cult#CultLeader) = 
    leader.cult.Follower(leader, "Fred") <-- THIS LINE FAILS TO COMPILE 
} 

In altre parole, la personalità di un CultLeader dovrebbe attrarre un seguace allo stesso culto come il CultLeader.

Il compilatore Scala 2.11.2 dice:

TypeProjection.scala:11: error: type mismatch; 
found : Cult#CultLeader 
required: leader.cult.CultLeader 
    leader.cult.Follower(leader, "Fred") 
         ^

Si compila e funziona correttamente se aggiungo un cast, come questo:

leader.cult.Follower(leader.asInstanceOf[leader.cult.CultLeader], "Fred") 

che sembra goffo e introduce run-time controllo per qualcosa che dovrebbe essere deducibile in fase di compilazione. Almeno ho una soluzione. Come posso ottenere che il compilatore Scala deduca che il tipo di leader è infatti leader.cult.CultLeader?

Preferirei non passare cult come un altro argomento a attractFollower. Nel mio codice attuale, ciò potrebbe comportare un brutto passaggio del parametro cult, quando in realtà non dovrebbe essere affatto superato.

risposta

9

Il modo più semplice è:

trait Cult { cult_ => 
    case class CultLeader(personality: Personality) { 
    val cult = cult_ 
    val follower = personality.attractFollower(this) 
    } 
    case class Follower(leader: Cult#CultLeader, name: String) // <-- Cult#CultLeader here 
} 

trait Personality { 
    def attractFollower(leader: Cult#CultLeader) = 
    leader.cult.Follower(leader, "Fred") 
} 

// Exiting paste mode, now interpreting. 

defined trait Cult 
defined trait Personality 

Qui sei in modo esplicito specificando che Follower può prendere qualsiasi proiezione, che si sta effettivamente cercando di forzare con asInstanceOf.


C'è un altro modo per farlo:

trait Cult { 
    case class CultLeader(personality: Personality) { 
    def fl(name: String) = Follower(this, name) 
    val follower = personality.attractFollower(this) 
    } 
    case class Follower(leader: CultLeader, name: String) 
} 

trait Personality { 
    def attractFollower(leader: Cult#CultLeader) = leader.fl("Fred") 
} 

o

trait Cult { 
    case class CultLeader(personality: Personality) { ld => 
    val follower = personality.attractFollower(this) 
    case class Follower(name: String) { val leader = ld } 
    } 
} 

trait Personality { 
    def attractFollower(leader: Cult#CultLeader) = leader.Follower("Fred") 
} 

UPDATE: Questo esempio potrebbe rendere più chiaro il motivo per Scala sta facendo quello che è

trait Cult { cult_ => 
    case class CultLeader(personality: Personality) { 
    val cult: Cult = cult_ //could be new Cult{} as well 
    val l = this.asInstanceOf[cult.CultLeader] //We have to do asInstanceOf here because Scala have no glue (from type signature) that this cult is same as cult_ 
    val follower = personality.attractFollower(this) 
    } 

    case class Follower(leader: CultLeader, name: String) 
} 

trait Personality { 
    def attractFollower(leader: Cult#CultLeader) = 
    leader.cult.Follower(leader.l, "Fred") 
} 

// Exiting paste mode, now interpreting. 
defined trait Cult 
defined trait Personality 

Ed ecco finale soluzione che fa quello che si vuole in modo corretto:

trait Cult { cult_ => 
    case class CultLeader(personality: Personality) { 
    val cult: cult_.type = cult_ 
    val l: cult.CultLeader = this 
    val follower = personality.attractFollower(this) 
    } 

    case class Follower(leader: CultLeader, name: String) 

} 

trait Personality { 
    def attractFollower(leader: Cult#CultLeader) = 
    leader.cult.Follower(leader.l, "Fred") 
} 

// Exiting paste mode, now interpreting. 
defined trait Cult 
defined trait Personality 

Il fermo qui è che cult_.type è dipendente dal percorso (proiettata).

+0

C'è un modo per richiedere che un follower provenga dallo stesso Culto del 'leader' del follower? –

+0

Sì, quella era la tua soluzione precedente, ma non puoi passare un leader di ** qualsiasi ** cult ad un follower che richiede un leader di ** concreto ** cult - non c'è modo di controllarlo in fase di compilazione come Scala non sa come si chiamerà esattamente il tuo 'attractFollower' (potrebbe essere un codice al di fuori del modulo di costruzione). Quindi la tua soluzione è come passare 'Any' quando la funzione richiede 'Int'. Quindi non c'è modo di richiedere una proiezione di culto concreta e passare una proiezione simultaneamente - è logicamente scorretto. – dk14

+0

Anche se 'attrFollower' può essere chiamato da qualsiasi luogo, non dovrebbe essere in grado di creare un Seguace dello stesso Culto del leader passato a 'attractFollower'? –

Problemi correlati