为什么Scala不能推断路径依赖类型的路径 - 即使是来自显式的自引用?

Ben*_*itz 13 scala type-inference path-dependent-type type-projection

我想将一个对象传递给一个接受带有投影类型的参数的函数,并让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
}
Run Code Online (Sandbox Code Playgroud)

换句话说,CultLeader的Personality应该吸引追随者到与CultLeader相同的邪教.

Scala 2.11.2编译器说:

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

如果我添加一个强制转换,它会正确编译并运行,如下所示:

    leader.cult.Follower(leader.asInstanceOf[leader.cult.CultLeader], "Fred")
Run Code Online (Sandbox Code Playgroud)

这似乎很笨拙,它引入了运行时检查,以便在编译时可以推导出来.至少我有一个解决方法.我怎样才能让Scala编译器推断出它leader的类型实际上是leader.cult.CultLeader什么?

我宁愿不cult作为另一个论点传递给attractFollower.在我的实际代码中,这可能会导致很多丑陋的传递cult参数 - 当它真的不需要传递时.

dk1*_*k14 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
Run Code Online (Sandbox Code Playgroud)

在这里,您明确指出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")
}
Run Code Online (Sandbox Code Playgroud)

要么

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")   
}
Run Code Online (Sandbox Code Playgroud)

更新:此示例可以更清楚地说明为什么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
Run Code Online (Sandbox Code Playgroud)

这是最终的解决方案,以正确的方式做你想要的:

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
Run Code Online (Sandbox Code Playgroud)

这里的问题cult_.type是路径依赖(预测).