-
Notifications
You must be signed in to change notification settings - Fork 834
[TINKERPOP-2234] - Proposal for Type Predicate & Type Enum #3207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.8-dev
Are you sure you want to change the base?
Conversation
|
||
==== Gremlin Type Enum | ||
|
||
We will define a `GType` enum consiste of core types in Gremlin be used with the type predicate (for full set of types, see Appendix), for type safety and maintainability across GLVs. This will also be the singel set of type enum used in Gremlin steps (e.g. replacing `N` in `asNumber()`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please include the hard line breaks?
note a few spelling mistakes: consiste, singel, extensable...please review
} | ||
---- | ||
|
||
=== Addition of `P.typeOf(token)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is implemented like Text
enum. Wouldn't it be PBiPredicate<Object, GType>
? I assume the various P.typeOf()
overloads would normalize to a GType
or is that not how it would work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm I might not understand how providers extend these, but I'd think if we are trying to test classes not defined in GType but registered into the cache (i.e. provider-defined), then they would not be able to normalize to GType? So we'd need to keep this <Object, Object> and assert types inside the implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we only need to think about Java here as Type
isn't really exposed to users. Like, it's PBiPredicate<Object,Class<?>>
where we normalize to a Java Class
, roughly:
public static <V> P<V> typeOf(final Class<?> value) {
return new P(new PBiPredicate<V, Class<?>>() {
@Override
public boolean test(V v, Class<?> aClass) {
return v.getClass().equals(aClass);
}
}, value);
}
that captures all the typeOf
variations that we have, while constraining P
to more than just Object
.
while we consider this syntax "sugar" to users because it's not available to all GLVs, it's actually better described as a feature of embedded Gremlin. perhaps that distinction needs to be an angle that is considered when adding new steps. we'd thus have considerations for:
- canonical
- embedded
- sugar
Addressing those as they pertain to each Gremlin implementation would help better isolate designs.
gremlin> g.V().values().is(not(typeOf(GType.NULL))) | ||
---- | ||
|
||
=== Extending Addition Type Tokens via Global Cache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is just a point of clarification but I thought that this was going to be like TraversalStrategy
grammar extensions. With the TraversalStrategy
implementation the grammar doesn't use strings. You can have a MyProviderStrategy
and refer to it directly:
g.withStrategies(MyProviderStrategy)
and even do more complex configured ones like:
g.withStrategies(MyProviderStrategy(config1: true, config2: ['a', 'b']))
I think we want the same thing here. In the grammar if a provider has a org.provider.Point
class it is registered in the cache and you can just do typeOf(Point)
in the grammar.
In Java if you have a Point
class to be able to do g.inject(new Point(1,2))
then why not just typeOf(Point.class)
in Java which converts internally to typeOf('org.provider.Point')
for execution in the grammar.
In cases where there is no provider class, like maybe .NET, users are just going to have to use the string form: TypeOf('org.provider.Point')
I do think this section however does raise an interesting point about name collisions. I largely ignored that for TraversalStrategy
sticking to keying the registry on the simple name for the class as chance of collisions seemed low. For types that might not be the case as I think I could see TinkerPop having a Point
and JanusGraph having a Point
. Perhaps though we use convention to handle this. TinkerPop retains the global namespace as a priority and providers can fit there stuff in around our naming. If they conflict, providers must prefix their types. So we would have Point
and JanusGraph would have jx:Point
. This body of work should clarify this usage in the provider docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thought here was to give freedom in defining tokens for grammar to recognize instead of using the default simple name. I had it originally when I was considering provider-defined enum tokens with customized prefixes (e.g. CType.XXX
), which would also avoid name collisions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, but that feels like it opens more doors than we know we need. Let me ask this, how does this align with PDTs then? You use Point
in this example. If we open a door to refer to Point
as MyPoint
for typeOf
is there also going to be a chance that users will refer to it as Point
in their code but then MyPoint
in the grammar?
// Use of String key in GlobalTypeCache | ||
gremlin> g.inject(1.0,2,3,'hello',false).is(P.typeOf("GType.NUMBER")).fold() // in this case | ||
==> [1.0,2,3] | ||
// Use of canonical Java name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume searches over the Java class name are restricted to the registry? in other words, you could do typeOf("java.awt.Color")
but unless it is registered, we won't instantiate the Class
for comparison, right? it would be good to clarify that here and in future docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently I'm thinking of instantiating the object regardless, but it does make more sense to only do so if registered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it should only be if its registered. it reduces the chance of someone doing something foul from a security perspective. i don't know what foul would be but no reason to leave a hole where there doesnt need to be one. please make sure that's clear in the proposal. thanks.
==> [1.0,2,3] | ||
gremlin> typeOf(Number).test(1) | ||
==>true | ||
gremlin> g.inject(1.0,2,3,'hello',false).or(is(typeOf(Integer)), is(typeOf(Boolean))).fold() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an alternative example where its doing P.or(...)
with typeOf
gremlin> g.V().hasLabel('person').values().is(typeOf(Integer)).sum() | ||
==>123 | ||
|
||
// Potentially used to ensure a sideeffect is properly applied: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So asNumber
will take a GType
in addition to N
? what happens if asNumber
takes a GType
that isn't referencing a numeric? filter i guess?
I've never been sure i like that we have different ways of referencing a number: GType
and N
. As I look at this example in particular, i like it less.
// the exact example mixes a few things with asNumber(GType) and typeOf(Class) from the java sugar.
// maybe people would do that....
g.V().hasLabel('person').
sideEffect(property('age', values('age').asNumber(GType.DOUBLE))).
values('age').is(typeOf(Double))
// approaching strictly from the grammar and GType we'd have the following which is nice and symmetric
g.V().hasLabel('person').
sideEffect(property('age', values('age').asNumber(DOUBLE))).
values('age').is(typeOf(DOUBLE))
// approaching strictly from the Java sugar its super nice - incidentally, i don't see the
// asNumber(Class<? extends Number>) overload, but we can see how nice that looks!!
g.V().hasLabel('person').
sideEffect(property('age', values('age').asNumber(Double))).
values('age').is(typeOf(Double))
// approaching strictly from the grammar and where someone uses N and GType with the
// canonical Gremlin as defined so far.
g.V().hasLabel('person').
sideEffect(property('age', values('age').asNumber(double_))).
values('age').is(typeOf(DOUBLE))
That last example seems problematic to me, particularly for those writing scripts. The competing types are going to be confusing in that case. Like, why do i sometimes use double_
and other times DOUBLE
? You could see folks mixing them up quite easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current thought is to replace the N
enum entirely, and only have GType
. For asNumber() there will be a check for numerical GType tokens and throw exception in this case.
If we want to keep N
, the tokens would either be entirely the same (i.e. DOUBLE
) as GType
, or GType
will not contain what N
has, for the reason of the last example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the proposal should be updated with this information.
For asNumber() there will be a check for numerical GType tokens and throw exception in this case.
runtime exception or traversal construction exception?
If we want to keep
N
Is there some idea to keep N
still? if so, why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some idea to keep N still? if so, why?
I would not think there is a reason to keep N
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
runtime exception or traversal construction exception?
Traversal construction, unless there are objections on throwing there.
Is there some idea to keep N still? if so, why?
No, I would not keep N
. It had been brought up as an idea that we can keep the types clean for asNumber() for compile time, but I'd think it's unnecessary to have separate enums, so the current consensus is to keep GType
only.
FLOAT(Float.class), | ||
INT(Integer.class), | ||
LIST(List.class), | ||
LONG(Long.class), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The examples below mention GType.NUMBER
, GType.VERTEX
, should those be added here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I took a subset to show as example, I didn't want to bloat the content here so the full spec is down in Appendix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please include a comment in the sample code here so that folks know to look in the appendix
// Use of Java class for sugar syntx | ||
gremlin> g.V().is(P.typeOf(Vertex.class)).fold() | ||
==> [v[1],v[2],[v3],[v4],[v5],[v6]] | ||
gremlin> g.inject(1.0,2,3,'hello',false).is(typeOf(Number)).fold() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this example and the ones below missing the .class
ie. typeOf(Number.class)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in java you need the .class
, but groovy is fine without. might be worth distinguishing that and as i'd alluded to elsewhere to try to not mix syntax .
Co-authored-by: andreachild <[email protected]>
As discussed previously (see links in the Jira), this proposal aims to add a
typeOf
predicate for asserting types in Gremlin.For maintainability, this also defines a set of core type enum (
GType
) to be used in Gremlin (which will replaceN
forasNumber()
), as well as an overload for String tokens, defined by a global cache for providers to register their own custom types, and to be recognized by the Grammar.VOTE +1