2015-07-13 16 views
13

Chciałbym przekazać obiekt do funkcji, która akceptuje argument z rzutowanym typem, i uzyskać Scala, aby wydedukować, że typ obiektu pochodzi z obiektu, który go otacza. Oto niektóre prosty kod do zilustrowania trudności:Dlaczego Scala nie może wyprowadzić ścieżki typu zależnego od ścieżki, nawet z wyraźnego samoodniesienia?

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 
} 

Innymi słowy, osobowości CultLeader powinna przyciągnąć zwolennika do tego samego Cult jako CultLeader.

Scala 2.11.2 kompilator mówi:

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

To kompiluje i działa poprawnie, jeśli mogę dodać obsadę, tak:

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

który wydaje niezdarny i wprowadza run-time sprawdzanie dla czegoś, co powinno być dedukowalne podczas kompilacji. Przynajmniej mam obejście. Jak mogę uzyskać kompilator Scala, aby wydedukować, że typ leader jest w rzeczywistości leader.cult.CultLeader?

Wolałbym nie przekazywać cult jako kolejnego argumentu dla attractFollower. W moim prawdziwym kodzie, mogłoby to spowodować wiele brzydkiego omijania parametru cult - kiedy naprawdę nie powinno być w ogóle potrzebne.

Odpowiedz

9

Prostym sposobem jest:

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 

Tutaj jesteś wyraźnie zaznaczając, że Follower może podjąć każdą projekcję, która jesteś rzeczywiście próbuje wymusić z asInstanceOf.


Nie ma innego sposobu, aby to zrobić:

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

lub

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: Ten przykład mogłoby uczynić go bardziej jasne, dlaczego Scala robi to, co ona robi:

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 

A oto końcowy rozwiązanie, które czyni to, co chcesz w prawidłowy sposób:

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 

Połów jest to, że jest ścieżka cult_.type zależne (prognozowanych).

+0

Czy istnieje sposób na wymaganie, aby wyznawca pochodził z tego samego kultu, co "przywódca" wyznawcy? –

+0

Tak, to było twoje oczywiste rozwiązanie - jednak nie możesz przekazać przywódcy ** jakiegokolwiek ** kultu dla naśladowcy, który wymaga przywódcy z ** betonu ** kultu - nie ma sposobu, aby to sprawdzić w czasie kompilacji Scala nie wie, jak dokładnie zostanie wywołany 'attractFollower' (może to być kod na zewnątrz modułu budowania). Zatem twoje rozwiązanie jest jak przekazanie 'Any', gdy funkcja wymaga' Int'. Nie ma więc potrzeby wymagać konkretnej projekcji kultowej i jednocześnie przekazywać projekcję - jest to logicznie niepoprawne. – dk14

+0

Mimo że 'attractFollower' może być wywołany z dowolnego miejsca, czy nie powinien być w stanie stworzyć wyznawcy tego samego kultu, co lider, który został przekazany do' attractFollower'? –

Powiązane problemy