I played with taking the changes a bit further, imagining withStrategies()
was removed and I found that I really don't like what it's doing to Gremlin
in Java. The type safety of the builders for strategies works too well.
It's starting to feel wrong to me to try to send Java down this path as it
conflicts with the idea that languages should express Gremlin in the syntax
comfortable to it. I think that is focusing me on solving this issue as a
point of Groovy in relation to script execution which was more the initial
form of this thread.

To this end i took a different approach which applies to Groovy and thus
script submissions:

gremlin> g = TinkerFactory.createTheCrew().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
gremlin> g = g.withStrategies(ReadOnlyStrategy, new
SubgraphStrategy(vertexProperties: __.hasNot('endTime')))
==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
gremlin> g.V().as('a').values('location').as('b').
......1>   select('a','b').by('name').by()
==>[a:marko,b:santa fe]
==>[a:stephen,b:purcellville]
==>[a:matthias,b:seattle]
==>[a:daniel,b:aachen]

I like that withStrategies() essentially takes a singleton Class or a new'd
up TraversalStrategy instance with named constructor arguments (names align
to the existing config/builder options). I think that syntax is what we
would likely expect from Groovy and is succinct for Gremlin "as a script".
It's also nice that we maintain the existing Java APIs without having to
introduce an overload there or change existing methods for strategy
construction. The code changes are fairly minimal in that respect.



On Mon, Nov 2, 2020 at 9:24 AM Stephen Mallette <[email protected]>
wrote:

> Decided to play with some syntax a bit just to see where this might go.
> Here is the original:
>
> gremlin> g.withStrategies(ReadOnlyStrategy.instance()).V().addE('knows')
> The provided traversal has a mutating step and thus is not read only:
> AddEdgeStep({label=[knows]})
> Type ':help' or ':h' for help.
> Display stack trace? [yN]n
> gremlin> g =
> g.withStrategies(SubgraphStrategy.build().vertexProperties(hasNot('endTime')).create())
> //2\
> ==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
> gremlin> g.V().as('a').values('location').as('b'). //3\
>            select('a','b').by('name').by()
> ==>[a:marko,b:santa fe]
> ==>[a:stephen,b:purcellville]
> ==>[a:matthias,b:seattle]
> ==>[a:daniel,b:aachen]
>
> Here is the revision:
>
> gremlin> g.withStrategy(ReadOnlyStrategy).V().addE('knows')
> The provided traversal has a mutating step and thus is not read only:
> AddEdgeStep({label=[knows]})
> Type ':help' or ':h' for help.
> Display stack trace? [yN]n
> gremlin> g = g.withStrategy(SubgraphStrategy, [vertexProperties:
> hasNot('endTime')])
> ==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
> gremlin> g.V().as('a').values('location').as('b'). //3\
> ......1>            select('a','b').by('name').by()
> ==>[a:marko,b:santa fe]
> ==>[a:stephen,b:purcellville]
> ==>[a:matthias,b:seattle]
> ==>[a:daniel,b:aachen]
>
> There is no tricky Groovy stuff going on here - I actually modified
> Gremlin in java to have:
>
> TraversalSource withStrategy(Class<? extends TraversalStrategy>
> traversalStrategy)
> TraversalSource withStrategy(Class<? extends TraversalStrategy>
> traversalStrategy,
>                              Map<String, Object> configuration)
>
> While there is something nice about the Builder pattern in Java, I think
> the chief issue is that it bloats the Gremlin language with more official
> syntax related to strategy construction. If we fall back to a more general
> Map configuration approach we lose some type safety and code auto-complete
> in Java but I think adding strategies is not a part of the daily work of
> Gremlin. It's a special configuration to the TraversalSource, so if we had
> to sacrifice a bit there I think we gain a lot more in language consistency.
>
> On Fri, Oct 30, 2020 at 3:08 PM Stephen Mallette <[email protected]>
> wrote:
>
>> I've been thinking a bit about TraversalStrategy declarations. There are
>> really two forms:
>>
>> 1. an easy no configuration option which is typically a singleton and is
>> just created by instance() like ReadOnlyStrategy.instance()
>> 2. a more complex option that uses builder pattern to construct the
>> traversal with build()...create() as in
>> ReservedKeysVerificationStrategy.build().reservedKeys(new
>> HashSet<String>(){{ add("id");}})
>>
>> The Builder pattern is kinda neat in that it's quite explicit API and
>> looks familiar in Java. It doesn't really translate though to other
>> languages so well, aside from C# perhaps. We've already gone to
>> constructors with named arguments in other languages like Python and
>> Javascript which is much more succinct. That leaves Groovy a bit on its own
>> and while Groovy could probably follow the Java path it doesn't look quite
>> right in practice (which means it doesn't really follow the premise of what
>> variants are about):
>>
>> g.withStrategies(ReservedKeysVerificationStrategy.build().reservedKeys(['id']
>> as Set).create()).V()
>>
>> or more groovy-er
>>
>> g.withStrategies(new
>> ReservedKeysVerificationStrategy(reservedKeys:['id'])).V()
>>
>> I think about these things because I've been looking at Translator
>> implementations lately and it is important that they generate the canonical
>> form of their variant so that we have shared consistency for what Gremlin
>> is in each language.
>>
>> Finally, consider that Groovy scripts are still a prevalent way of
>> shipping Gremlin around. I've often thought they might die out but they
>> remain persistent especially with REST APIs and I imagine even TinkerPop 4
>> won't be able to dislodge them. If so, Gremlin in Groovy is likely to be
>> the canonical string representation of Gremlin.
>>
>> For more random thoughts see:
>>
>> https://issues.apache.org/jira/browse/TINKERPOP-2461
>>
>> Anyone happen to have any thoughts on this matter?
>>
>

Reply via email to