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:
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? 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.
Currently for depth 2 I'm using:
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.
I allow myself to do some minor changes in your example to make it more reproducible. Inside
object PosnEndsI haveThing you need first is some simple typeclass like
Now you can introduce canonical elements for induction:
Finally you can define your implicit conversion
From this point
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, i.e 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.