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.

Другие вопросы по тегам