Search code examples
scalagremlinshapeless

Shapeless and gremlin scala: How do I return the result of a call to `as`?


So, I am calling this function as (from gremlin-scala):

case class GremlinScala[End, Labels <: HList](traversal: GraphTraversal[_, End]) {
  def as(name: String, moreNames: String*)(implicit p: Prepend[Labels, End :: HNil]) =
    GremlinScala[End, p.Out](traversal.as(name, moreNames: _*))
}

It is defined here: https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala#L239

It takes an implicit Prepend argument, which I am not sure I understand. I know that gremlin-scala uses its HList to keep track of which points in the query as is called, so that later when select is called it knows which points in the traversal to return.

This is the key: as appends to that HList. Or prepends apparently, as the case may be.

This works fine in general code, but now I want to write a function that calls as and returns its result. This is where I am stuck: what is the signature of the return value of this function?

Finally, I have added an implicit param to my function, but I fear I have just chased the problem up a level. Here is what I have so far:

case class AsOperation[A, In <: HList](step: String) extends Operation {
  def operate(g: GremlinScala[A, In]) (implicit p: Prepend[In, ::[A, HNil]]): GremlinScala[A, p.Out] = {
    g.as(step)
  }
}

This makes it compile, but I still can't use this function! Whenever I call it, it complains to me that

could not find implicit value for parameter p: shapeless.ops.hlist.Prepend[In,shapeless.::[A,shapeless.HNil]]

How do I write a function that returns the result of as, and what is its signature?

Thanks!


Solution

  • As you explain correctly, the reason we use prepend is to keep the types of the steps labelled with as. It's keeping them in the reverse order since it's easier to process on both sides: for capturing and replaying.

    So the implicit p: Prepend[Labels, End :: HNil] is prepending the type at the current step so that we have it captured in the second type parameter (and can use it in later steps, e.g. select).

    As far as I can see you're doing exactly the right thing, and it actually works... for me anyway :)

    This compiles:

    import gremlin.scala._
    import shapeless.{HNil, ::}
    import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory
    def graph = TinkerFactory.createModern.asScala
    val gs1: GremlinScala[Vertex, Vertex :: HNil] = graph.V().as("a")
    val gs2: GremlinScala[Vertex, Vertex :: HNil] = AsOperation("someLabel").operate(graph.V())