Hi Yura, welcome to the dev list and thanks for offering this thought for discussion.
> wouldn't it be nice to make Gremlin (or rather GLVs) a bit more type safe? You mention GLVs and I'd say that the choice to be more type safe would rest on the level of type safety features the language allows. If a language has type safety measures, it's nice when you can utilize them. I'll assume that you're talking about Java mostly though given your examples and say that we had a number of discussions around this in the early days of TinkerPop 3.x (which I don't remember the details of) and ultimately we went with what we have. My gut reaction to a complex inherited interface system is a bit on the repulsive side given some experience with TinkerPop 2.x and the weave of interfaces we had there which Marko and I vowed not to repeat. :) With your example with by() I'm not sure it fully solves the problem you pose though because steps that support by() don't always support all overloads of by(). And then you have other modulators, like with() which you might mix with by() for some steps but not others. Before you know it there will be a mad mix of different Traversal interfaces species (i.e. see the Traverser hierarchy which almost no one understands but at least is manageable and fits its purpose) with lots of deprecation from version to version. Maybe I'm envisioning it wrong, but it seems like sacrificing that portion of type safety for a clean interface hierarchy is a good choice. On Tue, Aug 14, 2018 at 3:46 PM [email protected] <[email protected]> wrote: > Hello guys, > > I have small question to discuss (sorry if it was already brought up, > couldn't find it on the lists) - wouldn't it be nice to make Gremlin (or > rather GLVs) a bit more type safe? > Lets consider Gremlin java for example. After each step we return > GraphTraversal which contains all available Gremlin steps. So we can write > something like 'g.V().out().by("name")' which will produce CCE. What if we > would return not GraphTraversal (with all steps) but rather interface with > subset of steps which are relevant after last one. > For example after Modulating steps return Traversal with common steps and > By steps: > > default <E2> ByModulatingGraphTraversal<S, Map<String, E2>> project(final > String projectKey, final String... otherProjectKeys) { > ... > return (ByModulatingGraphTraversal) this.asAdmin().addStep(new > ProjectStep(...)); > } > > where: > > interface BaseWithByTraversal extends BaseGraphTraversal, > ByModulatingGraphTraversal {} > > interface BaseGraphTraversal<S, E> extends Traversal<S, E> { > // common steps > } > > interface ByModulatingGraphTraversal<S, E> { > // by stesps > default BaseWithByTraversal<S, E> by() { ... } > > default BaseWithByTraversal<S, E> by(final Traversal<?, ?> traversal) > { ... } > ... > } > > or even: > default <E2, G extends BaseGraphTraversal<S, E2> & > ByModulatingTraversal<S, E2>> G project... > > It can be useful in several cases: > 1) by modulating steps -> By steps > 2) order -> By (with order direction) > 3) configuring steps -> with + common steps > 4) addE -> to/from + common > 5) io -> read / write > 6) path -> from/to + common > 7) ... > > I realize that this is breaking (destroying?) change with relatively low > benefit. So maybe at least it is something to consider for TinkerPop4? > > Thanks and regards, > -Yura > > P.S. sorry for poorly formatted long message > P.S.S. we have VerificationStrategies but its run time vs compile time > P.S.S.S. this question was inspired by jOOQ (https://github.com/jOOQ/jOOQ) > which allows to write type safe SQL in Java > >
