Closed yusefnapora closed 8 years ago
LGTM
This is great, overall -- maybe we should try to upstream this!
My one concern is the >>
boundary -- how do we decide what goes inside that block versus previous steps? You could technically put everything into >>
, right?
I guess you could technically use >>
for everything, since the T
param is so open-ended... I've been thinking of >>
as taking you out of GremlinScala
land, but that's not really enforced by the types.
I guess the main distinction between them is the precedence, so it makes sense to use >>
for the last operation in the chain... But maybe we only need >>
instead of two operators?
I kind of want them to be more restrictive, not less, but there's no obvious way to formalize this rule. What I'd want to capture is that you're only calling "one" function (not that this is a very meaningful notion) -- maybe it's OK to keep this a matter of convention.
Keeping ~>
restrictive is good, makes sure you use valid steps
agreed
So, I figured out a way to make the >>
operator work for any type except GremlinScala
, to enforce that you're using it to get a value out of the pipeline.
It's not especially pretty... If you do this:
object Implicits {
sealed trait NotAGremlinScala[T]
implicit def anything[T <: Any] = new NotAGremlinScala[T] {}
implicit def conflictForGremlinScala[T <: GremlinScala[_, _]] =
new NotAGremlinScala[T] {}
implicit def conflictForGremlinScala2[T <: GremlinScala[_, _]] =
new NotAGremlinScala[T] {}
// ...
implicit class GremlinScalaImplicits[End, Labels <: HList](val gs: GremlinScala[End, Labels]) {
def >>[T: NotAGremlinScala](f: GremlinScala[End, Labels] => T): T = f(gs)
}
}
It sets up an implicit conversion from Any
to NotAGremlinScala
. But the two conflicting implicit conversions from GremlinScala[_, _]
to NotAGremlinScala
will cause a compiler error if you try to actually use GremlinScala
. The compiler error isn't especially helpful:
g.V >> canonicalsWithID(someID)
Main.scala:226: ambiguous implicit values:
both method conflictForGremlinScala in object Implicits of type [T <: gremlin.scala.GremlinScala[_, _]]=> io.mediachain.Traversals.Implicits.NotAGremlinScala[T]
and method conflictForGremlinScala2 in object Implicits of type [T <: gremlin.scala.GremlinScala[_, _]]=> io.mediachain.Traversals.Implicits.NotAGremlinScala[T]
match expected type io.mediachain.Traversals.Implicits.NotAGremlinScala[gremlin.scala.GremlinScala[AnyRef,shapeless.HNil]]
but it will enforce the usage we want. If we drop some comments in explaining the weird implicit trickery, I might be alright with it
imo the witness is good. shall we merge @yusefnapora @parkan ?
sure, works for me. I can push up the implicit witness stuff; it's off in a local branch atm
Will review in the next couple of hrs
Looks like it improves code readability, as it reduces the density.
this is great, nice work yusef
This adds two implicit operators to the
GremlinScala
type.~>
feeds aGremlinScala[Vertex, Labels]
into a function ofGremlinScala[Vertex, OutLabels]
. This lets you write chains like this:Due to scala's parsing rules, the
~>
operator must be at the end of the line if you're breaking the operations onto multiple lines. So unfortunately this won't work:The other operator is
>>
This is used for pulling a value out of the pipeline:I pulled the various "traverse and extract" methods out of the implicit
GremlinScala
scope, and rewrote them as free functions ofGremlinScala[Vertex, Labels] => T
, so they can be used with>>
If you want to call a
GremlinScala
method on the result of a~>
chain directly you either have to wrap the~>
chain in parens:or use the
>>
operator with an anonymous function: