【问题标题】:Scala, gremlin-scala, HLists, Poly2, RightFold and a missing implicit PrependScala、gremlin-scala、HLists、Poly2、RightFold 和缺少的隐式 Prepend
【发布时间】:2017-04-06 20:36:16
【问题描述】:

所以,我试图将来自gremlin-scala 的一系列操作封装到HList 中,这样我就可以对它们执行RightFold(这将允许我将gremlin 查询构造为数据:特别是@987654325 @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))

这一切都很好,但我希望能够将查询构造为数据。我将其建模为OperationsHList,如下所示:

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 是什么意思,我为什么需要它?
  • 为什么在正常调用as时能找到隐含的Prepend,但在尝试RightFold时却失败了?
  • 如何创建Prepend 的隐式实例?
  • 创建后,如何将其传递给operate 的调用?
  • 这样做的正确方法是什么??

我可能还有更多问题,但这些是主要问题。我一直在阅读有关类型级编程和一般无形编程的文章,我真的很喜欢它,但是这种东西令人抓狂。我知道我在这里遗漏了一些微妙的类型,但很难知道从哪里开始解读遗漏的内容。

感谢您的帮助!我真的很想爱scala和shapeless,希望尽快克服这个障碍。

编辑:我做了一个最小的 repo,在这里重现了这个问题:https://github.com/bmeg/leprechaun

希望对您有所帮助!

【问题讨论】:

    标签: scala types gremlin shapeless hlist


    【解决方案1】:

    您的误解是 Prepend 的使用。编译器会自动为你生成它,你不需要手动创建它。

    Shapeless and gremlin scala: How do I return the result of a call to `as`? 中所述,Prepend 用于保留标记步骤的类型。 gremlin-scala readme.md 更深入。

    编译器实际上会准确地告诉您它需要什么: could not find implicit value for parameter p: shapeless.ops.hlist.Prepend[In,shapeless.::[A,shapeless.HNil]]

    这就是我所做的:在范围内添加一个隐式 Prepend :) 我刚刚给你发了一个PR,它现在编译得很好。

    PS:您可能想要更新您的 gremlin-scala 版本。

    【讨论】:

    • 有趣...现在我调用此as 方法的任何函数都需要声明隐式Prepend。有什么办法可以避免这种无限倒退? Prepend 是在什么时候实际创建的?
    猜你喜欢
    • 2012-01-10
    • 2020-12-08
    • 1970-01-01
    • 2021-08-25
    • 2019-08-12
    • 2014-08-23
    • 1970-01-01
    • 1970-01-01
    • 2022-11-15
    相关资源
    最近更新 更多