mediachain / L-SPACE

[DEPRECATED] Books = Knowledge = Power = (Mass x Distance^2) / Time^3
MIT License
9 stars 1 forks source link

Infix operators for query chaining and value extraction #59

Closed yusefnapora closed 8 years ago

yusefnapora commented 8 years ago

This adds two implicit operators to the GremlinScala type.

~> feeds a GremlinScala[Vertex, Labels] into a function of GremlinScala[Vertex, OutLabels]. This lets you write chains like this:

graph.V ~>
  imageBlobsWithExactMatch(someImage) ~>
  getAuthor

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:

graph.V 
  ~> imageBlobsWithExactMatch(someImage) 
  ~> getAuthor

The other operator is >> This is used for pulling a value out of the pipeline:

val canonicalXor = graph.V ~> personBlobsWithExactMatch(pablo) >> findCanonicalXor

I pulled the various "traverse and extract" methods out of the implicit GremlinScala scope, and rewrote them as free functions of GremlinScala[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:

(graph.V ~> foo).headOption

or use the >> operator with an anonymous function:

graph.V ~> foo >> (_.headOption)
bigs commented 8 years ago

LGTM

parkan commented 8 years ago

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?

yusefnapora commented 8 years ago

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?

parkan commented 8 years ago

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

bigs commented 8 years ago

agreed

yusefnapora commented 8 years ago

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

bigs commented 8 years ago

imo the witness is good. shall we merge @yusefnapora @parkan ?

yusefnapora commented 8 years ago

sure, works for me. I can push up the implicit witness stuff; it's off in a local branch atm

parkan commented 8 years ago

Will review in the next couple of hrs

vyzo commented 8 years ago

Looks like it improves code readability, as it reduces the density.

parkan commented 8 years ago

this is great, nice work yusef