1

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

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

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

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]]] 等将被隐式转换为 PosnEnds 特征?在实践中,我可能不需要很大的深度,但最好使用一个优雅的解决方案,隐式转换任意深度的 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)     
}
4

1 回答 1

7

是的。你可以用类型类中介来做到这一点。

我允许自己在您的示例中进行一些小的更改,以使其更具可重复性。里面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

编译并运行成功

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

所以我们使用了一些无值类型,即只使用隐式参数就可以实现的东西,这意味着可以由编译器构造。然后才将这个逻辑投射到具体的价值上。

于 2015-11-24T15:16:40.237 回答