Scala, gremlin-scala, HLists, Poly2, RightFold и отсутствующий неявный Prepend
Итак, я пытаюсь инкапсулировать серию операций из gremlin-scala
в HList
так что я могу сделать RightFold
над ними (это позволило бы мне построить запрос Гремлина как данные: HList
из Operations
).
Вот что я имею в виду: обычно вы можете позвонить gremlin-scala
вот так:
import gremlin.scala._
import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory
def graph = TinkerFactory.createModern.asScala
graph.V.hasLabel("person").out("created").as("creations").toList.map(_.valueMap)
---> List[Map[String,Any]] = List(Map(name -> lop, lang -> java), Map(name -> ripple, lang -> java), Map(name -> lop, lang -> java), Map(name -> lop, lang -> java))
Это все хорошо, но я хочу иметь возможность построить запрос в виде данных. Я моделирую это как HList
из Operations
вот так:
sealed trait Operation
case class VertexOperation[Labels <: HList](vertex: String) extends Operation {
def operate(graph: Graph): GremlinScala[Vertex, Labels] = {
graph.V.hasLabel(vertex).asInstanceOf[GremlinScala[Vertex, Labels]]
}
}
case class OutOperation[Labels <: HList](out: String) extends Operation {
def operate(vertex: GremlinScala[Vertex, Labels]): GremlinScala[Vertex, Labels] = {
vertex.out(out)
}
}
Тогда я мог бы создать запрос, поместив их в HList
вот так:
import shapeless._
val query = OutOperation("created") :: VertexOperation("person") :: HNil
Теперь, когда у меня есть это в HList
Я могу сделать RightFold
чтобы применить их один за другим к графику:
trait ApplyOperationDefault extends Poly2 {
implicit def default[T, L <: HList] = at[T, L] ((_, acc) => acc)
}
object ApplyOperation extends ApplyOperationDefault {
implicit def vertex[T, L <: HList, S <: HList] = at[VertexOperation[S], Graph] ((t, acc) => t.operate(acc))
implicit def out[T, L <: HList, S <: HList] = at[OutOperation[S], GremlinScala[Vertex, S]] ((t, acc) => t.operate(acc))
}
object Operation {
def process[Input, Output, A <: HList](operations: A, input: Input) (implicit folder: RightFolder.Aux[A, Input, ApplyOperation.type, Output]): Output = {
operations.foldRight(input) (ApplyOperation)
}
}
И назовите это так:
val result = Operation.process(query, graph).toList
Это все работает! И показывает большое обещание.
Вот где я добираюсь до своей проблемы: когда я пытаюсь сделать это с as
операция, я могу получить Operation
Скомпилировать:
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)
}
}
(Я добавил, что (implicit p: Prepend[In, ::[A, HNil]])
там, потому что компилятор жаловался иначе)... но когда я пытаюсь создать неявный обработчик для этого случая вместе с другими, он терпит неудачу:
implicit def as[T, L <: HList, A, In <: HList] = at[AsOperation[A, In], GremlinScala[A, In]] ((t, acc) => t.operate(acc))
---> could not find implicit value for parameter p: shapeless.ops.hlist.Prepend[In,shapeless.::[A,shapeless.HNil]]
Итак, несколько вопросов здесь:
- Что это неявно
Prepend
все о, а зачем мне это? - Почему он может найти неявное
Prepend
при звонкеas
нормально, но не удается при попыткеRightFold
над ним? - Как мне создать неявный экземпляр
Prepend
? - После создания, как бы я передал это вызову
operate
? - Как правильно это сделать??
У меня, наверное, есть еще вопросы, но это главные. Я читал о программировании на уровне типов и вообще бесформенный, и мне это действительно нравится, но такие вещи сводят с ума. Я знаю, что здесь есть некая тонкая вещь, которую мне не хватает, но трудно понять, с чего начать расшифровку того, чего не хватает.
Спасибо за любую помощь! Я действительно хочу любить скалу и бесформенный, надеясь скоро преодолеть это препятствие.
РЕДАКТИРОВАТЬ: я сделал минимальное репо, воспроизводя проблему здесь: https://github.com/bmeg/leprechaun
Надеюсь, это поможет!
1 ответ
Ваше недоразумение заключается в использовании Prepend. Компилятор сгенерирует его для вас автоматически, вам не нужно создавать его вручную.
Как упоминалось в Shapeless и gremlin scala: как мне вернуть результат вызова в `as`? Prepend
используется для хранения типов помеченных шагов. Readme.md gremlin-scala идет вглубь.
Компилятор фактически говорит вам, что именно нужно: could not find implicit value for parameter p: shapeless.ops.hlist.Prepend[In,shapeless.::[A,shapeless.HNil]]
Вот что я сделал: добавил неявный Prepend в сферу:) Я только что отправил вам пиар, теперь он прекрасно компилируется.
PS: вы можете обновить свои версии gremlin-scala.