fancadescala/tagless/src/main/scala/tf/bug/fancadetagless/FancadeF.scala

86 lines
2.8 KiB
Scala

package tf.bug.fancadetagless
import cats._
import cats.implicits._
import higherkindness.droste.data._
import polymorphic._
import scalax.collection.Graph
import scalax.collection.GraphEdge.DiHyperEdge
import tf.bug.fancadegraph.Block
import tf.bug.fancadegraph.Level
import tf.bug.fancadegraph.Pin
import tf.bug.fancadegraph.PinDefinition
import tf.bug.fancadegraph.Template
import tf.bug.fancadescodec.Position
sealed trait FancadeF[A] {
val template: Exists[Template]
val pins: Vector[PinDefinition]
}
object FancadeF {
implicit val fancadeTraverse: Traverse[FancadeF] = new Traverse[FancadeF] {
override def traverse[G[_]: Applicative, A, B](fa: FancadeF[A])(f: A => G[B]): G[FancadeF[B]] =
fa match {
case Capture(t, p) => Capture[B](t, p).pure[G].widen
case Use(c, t, i, o) =>
c.traverse(f).map { newC => Use(newC, t, i, o) }
}
override def foldLeft[A, B](fa: FancadeF[A], b: B)(f: (B, A) => B): B =
fa match {
case Capture(_, _) => b
case Use(c, _, _, _) => c.foldLeft(b)(f)
}
override def foldRight[A, B](fa: FancadeF[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa match {
case Capture(_, _) => lb
case Use(c, _, _, _) => Traverse[Vector].foldRight(c, lb)(f)
}
}
case class Capture[A](template: Exists[Template], pins: Vector[PinDefinition]) extends FancadeF[A]
case class Use[A](children: Vector[A], template: Exists[Template], inputs: Vector[PinDefinition], pins: Vector[PinDefinition]) extends FancadeF[A]
def render(f: Fix[FancadeF]): Level = {
val pc = PointerChain.deduplicate[Id, FancadeF](f)
val pcvi = pc.vertices.zipWithIndex
val blocks: Set[Block] = pcvi.map {
case (block, y) =>
Block(
Position(0, y, 0),
block.template
)
}.toSet
val wires: Graph[Pin, DiHyperEdge] =
pcvi.foldLeft(Graph.empty[Pin, DiHyperEdge]) {
case (graph, (block, y)) =>
block match {
case Capture(_, _) => graph
case Use(children, template, inputs, _) =>
val thisBlock = Block(
Position(0, y, 0),
template
)
val thesePins = inputs.map(Pin(thisBlock, _))
val childrenPins: Vector[Pin] = children.flatMap { child =>
val childInst = pc.vertices(child)
val childBlock = Block(
Position(0, child, 0),
childInst.template
)
childInst.pins.map(Pin(childBlock, _))
}
val newEdges: Vector[DiHyperEdge[Pin]] = childrenPins.zip(thesePins).map {
case (from, to) => DiHyperEdge(from, to)
}
graph ++ newEdges
}
}
Level(blocks, wires)
}
}