Подтвердить что ты не робот

Почему can not 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
}

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

Компилятор Scala 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 - когда это действительно не нужно передавать вообще.

4b9b3361

Ответ 1

Простым способом является:

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

И вот решение final, которое делает то, что вы хотите правильным способом:

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 зависит от пути (проецируется).