Scala:递归隐式类型



我有以下解析特征,为对象的开头和结尾提供文件位置:

case class FilePosn(lineNum :Int, tabs: Int, spaces: Int, fileName: String)
{/*code omitted*/}
trait PosnEnds
{
  def startPosn: FilePosn
  def endPosn: FilePosn
  def multiLine: Boolean = startPosn.lineNum != endPosn.lineNum
  def OneLine: Boolean = startPosn.lineNum == endPosn.lineNum
  def indent: Int = startPosn.tabs
  def startLine: Int = startPosn.lineNum
  def endLine: Int = endPosn.lineNum
}
object FilePosnVoid extends FilePosn(0, 0, 0, "There is no File position")
{ override def posnString(indentSize: Int): String = "No File Posn: " }

在伴随对象中,我创建了一个隐式,因此PosEnds序列本身就是隐式PosEnds:

object PosnEnds
{
  implicit class ImpPosnEndsSeq[A <: PosnEnds](thisSeq: Seq[A]) extends PosnEnds
  {
    override def startPosn: FilePosn = thisSeq.fHead(FilePosnVoid, (h, t) => h.startPosn)
    override def endPosn: FilePosn = thisSeq.fLast(FilePosnVoid, _.endPosn)     
  }
}

是否有递归使用隐词的方法,以便将Seq[Seq[a]]和Seq[Seq[Seq[a]]]等隐式转换为PosEneds特征在实践中,我可能不需要巨大的深度级别,但最好使用一个优雅的解决方案,隐式转换任意深度的Seq。

目前我使用的深度2:

implicit class ImpPosnEndsSeqSeq[A <: PosnEnds](thisSeq: Seq[Seq[A]]) extends PosnEnds
{
  override def startPosn: FilePosn = thisSeq.fHead(FilePosnVoid, (h, t) => h.startPosn)
  override def endPosn: FilePosn = thisSeq.fLast(FilePosnVoid, _.endPosn)     
}

是。你可以用typeclass中介器来完成。

我允许自己在你的例子中做一些小的修改,使其更具可复制性。在object PosnEnds内部,我有

val void = new FilePosn(0, 0, 0, "There is no File position") {
  override def posnString(indentSize: Int): String = "No File Posn: "
}
def empty = new PosnEnds {
  def startPosn: FilePosn = void
  def endPosn: FilePosn = void
}

你首先需要的是一些简单的类型类,比如

trait MakePosnEnds[X] extends (X => PosnEnds)

现在,您可以介绍用于归纳的规范元素:

implicit object idMakePosnEnds extends MakePosnEnds[PosnEnds] {
  def apply(x: PosnEnds) = x
}
implicit def seqMakePosnEnds[X](implicit recur: MakePosnEnds[X]) = new MakePosnEnds[Seq[X]] {
  def apply(x: Seq[X]): PosnEnds = new PosnEnds {
    val thisSeq = x.map(recur)
    override def startPosn: FilePosn = thisSeq.headOption.fold(void)(_.startPosn)
    override def endPosn: FilePosn = thisSeq.lastOption.fold(void)(_.endPosn)
  }
}

最后,您可以定义您的隐式转换

implicit def toPosnEnds[X](x: X)(implicit make: MakePosnEnds[X]): PosnEnds = make(x)

从这一点

Seq(Seq(Seq(empty))).startLine

成功编译并运行

与您的尝试的主要区别在于:我们不等待隐式转换进行堆栈。隐式解析可以是递归的,但隐式转换不能。

因此,我们使用了一些无值类型,即只使用隐式参数就可以实现的类型,这意味着可以由编译器构建。只有这样,才能将这种逻辑投射到具体的价值中。

相关内容

  • 没有找到相关文章

最新更新