2015-07-13 10 views
13

Ich würde gerne ein Objekt an eine Funktion übergeben, die ein Argument mit einem projizierten Typ akzeptiert, und Scala den Schluss ziehen, dass der Typ des Objekts von dem Objekt kommt, das es umgibt. Hier ist ein einfacher Code, die Schwierigkeit zu veranschaulichen:Warum kann Scala nicht den Pfad eines pfadabhängigen Typs ableiten - selbst von einer expliziten Selbstreferenz?

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 
} 

Mit anderen Worten, ein CultLeader Persönlichkeit einen Nachfolger zum gleichen Kult wie die CultLeader anziehen sollte.

Die Scala 2.11.2 Compiler sagt:

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

Es kompiliert und ordnungsgemäß ausgeführt wird, wenn ich eine Besetzung, wie folgt hinzufügen:

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

Das ungeschickt scheint und es führt zur Laufzeit überprüft für etwas, das zur Kompilierzeit ableitbar sein sollte. Zumindest habe ich einen Workaround. Wie kann ich den Scala-Compiler dazu bringen, daraus abzuleiten, dass der Typ leader tatsächlich leader.cult.CultLeader ist?

Ich würde lieber cult als ein weiteres Argument zu attractFollower übergeben. In meinem tatsächlichen Code könnte das zu einer Menge hässlicher Umgehung des Parameters cult führen - wenn es wirklich überhaupt nicht übergeben werden sollte.

Antwort

9

Der einfache Weg ist:

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 

Hier sind Sie explizit angeben, dass Follower jede Projektion nehmen, die Sie tatsächlich mit asInstanceOf zu zwingen versucht.


Es gibt eine andere Art und Weise zu tun, dass:

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") 
} 

oder

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: Dieses Beispiel es mehr machen konnte klar, warum Scala tut, was sie ist dabei:

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 

Und hier ist letzte Lösung, die tut, was Sie in der richtigen Art und Weise wollen:

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 

Der Haken dabei ist, dass cult_.type ist pfadabhängig (in Vorbereitung).

+0

Gibt es eine Möglichkeit zu verlangen, dass ein Follower vom gleichen Kult wie der "Anführer" des Anhängers ist? –

+0

Ja, das war deine vorherige Lösung - aber du kannst keinen Anführer von ** irgendeinem ** Kult an einen Anhänger weitergeben, der einen Anführer von ** konkretem ** Kult erfordert - es gibt keine Möglichkeit, es während der Kompilierung zu überprüfen Scala weiß nicht, wie genau Ihr 'attractFollower' aufgerufen wird (es könnte ein Code außerhalb des Build-Moduls sein). Ihre Lösung besteht also darin, 'Any' zu übergeben, wenn die Funktion' Int' erfordert. Also keine Möglichkeit, eine konkrete Kultprojektion zu verlangen und gleichzeitig eine Projektion zu passieren - das ist logisch falsch. – dk14

+0

Auch wenn 'attractFollower' von überall aufgerufen werden kann, sollte es nicht in der Lage sein, einen Follower des gleichen Kults wie den Leader zu erstellen, der an 'attractFollower' weitergegeben wurde? –

Verwandte Themen