我有以下解析特征,为对象的开头和结尾提供文件位置:
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
成功编译并运行
与您的尝试的主要区别在于:我们不等待隐式转换进行堆栈。隐式解析可以是递归的,但隐式转换不能。
因此,我们使用了一些无值类型,即只使用隐式参数就可以实现的类型,这意味着可以由编译器构建。只有这样,才能将这种逻辑投射到具体的价值中。