简体   繁体   English

Scala:递归隐式类型

[英]Scala: recursive implicit type

I have the following traits for parsing that give file positions for the beginning and end of the object: 我具有以下特征来进行解析,这些特征赋予对象开始和结束的文件位置:

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

In the companion object I create an implicit, so sequences of PosnEnds are themselves implicitly PosnEnds: 在同伴对象中,我创建一个隐式对象,因此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)     
  }
}

Is there anyway to use implicits recursively so a Seq[Seq[A]] and a Seq[Seq[Seq[A]]] etc will be implicitly converted to a PosnEnds trait? 无论如何,是否有递归使用隐式函数,以便将Seq [Seq [Aeq]]和Seq [Seq [Seq [Aeq]]]等隐式转换为PosnEnds特性? In practice I probably won't need huge levels of depth, but it would be nice to use an elegant solution that implicitly converted Seq of arbitrary depth. 实际上,我可能不需要大量的深度,但是最好使用一个优雅的解决方案来隐式转换任意深度的Seq。

Currently for depth 2 I'm using: 目前,对于深度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)     
}

Yes. 是。 You could do it with typeclass mediator. 您可以使用typeclass中介程序来完成。

I allow myself to do some minor changes in your example to make it more reproducible. 我允许自己对您的示例进行一些小的更改,以使其更具可重复性。 Inside object PosnEnds I have 我有内部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
}

Thing you need first is some simple typeclass like 您首先需要的是一些简单的类型类,例如

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

Now you can introduce canonical elements for induction: 现在,您可以介绍归纳的规范元素:

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)
  }
}

Finally you can define your implicit conversion 最后,您可以定义隐式转换

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

From this point 从这一点

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

compiles and runs succesfully 编译并成功运行

Major difference with your attempt: we dont wait implicit conversion to stack. 您尝试的主要区别:我们不等待隐式转换到堆栈。 Implicit resolution can be recursive, but implicit conversion can not. 隐式解析可以是递归的,但隐式转换不能。

So we are using some value-less type, ie something that could be achieved using only implicit arguments which means could be constructed by the compiler. 因此,我们正在使用某种无值类型,即可以仅使用隐式参数来实现的方式,这意味着可以由编译器构造。 And only then projecting this logic to the concrete value. 然后才将这种逻辑投射到具体的价值上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM