13

Я хотел бы передать объект функции, которая принимает аргумент с проецируемым типом, и заставить Scala вывести, что тип объекта поступает от объекта, который его охватывает. Вот некоторые простой код, чтобы проиллюстрировать сложность:Почему Scala не может определить путь зависимого от пути типа - даже из явной самореференции?

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 
} 

Другими словами, индивидуальность CultLeader должна привлечь последователя к тому же Cult как CultLeader.

Скала 2.11.2 компилятор говорит:

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

Он компилируется и работает правильно, если добавить оттенок, например:

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

Это кажется неуклюжим и вводит проверку во время выполнения для чего-то, что должно быть выведено во время компиляции. По крайней мере, у меня есть обходной путь. Как я могу заставить компилятор Scala вывести, что тип leader - это действительно leader.cult.CultLeader?

Я бы предпочел не пропускать cult как еще один аргумент attractFollower. В моем действительном коде это может привести к множеству уродливых проходов вокруг параметра cult - когда это действительно не нужно передавать вообще.

ответ

9

Простой способ:

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 

Здесь вы явно указать, что Follower может принимать любую проекцию, которую вы на самом деле пытаетесь заставить с asInstanceOf.


Существует еще один способ сделать это:

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

или

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: Этот пример может сделать его более понятным, почему Scala делает то, что она осуществление:

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 

А вот окончательное решение, которое делает то, что вы хотите в правильном пути:

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 

Загвоздка в том, что cult_.type линейно-зависимыми (проектируется).

+0

Есть ли способ требовать, чтобы последователь был от того же Культа, что и «лидер» последователя? –

+0

Да, это было ваше безоговорочное решение - однако вы не можете передать лидеру ** любого ** культа последователю, который требует лидера от ** конкретного ** культа - нет способа проверить его во время компиляции, поскольку Scala не знает, как именно будет вызван ваш 'attractiveFollower' (это может быть код вне модуля сборки). Таким образом, ваше решение похоже на передачу 'Any', когда функция требует' Int'. Таким образом, не нужно требовать конкретной культовой проекции и пропускать любую проекцию одновременно - это логически неверно. – dk14

+0

Даже если 'привлечьFollower' можно вызывать из любой точки мира, разве он не сможет создать последователя того же Культа, что и лидер, который был передан' attractiveFollower'? –