You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by co...@apache.org on 2025/09/11 01:29:22 UTC

(tinkerpop) branch GValue-master updated (cae12f2147 -> 7fbdff08c6)

This is an automated email from the ASF dual-hosted git repository.

colegreer pushed a change to branch GValue-master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


 discard cae12f2147 GValue and Antlr Parsing Cache (#3157)
     add 7294ae70d3 CTR Fix UUID arg handling in GremlinLang
     new 7fbdff08c6 GValue and Antlr Parsing Cache (#3157)

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (cae12f2147)
            \
             N -- N -- N   refs/heads/GValue-master (7fbdff08c6)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../translator/GroovyTranslateVisitor.java         | 47 ++++++++++++++--------
 .../language/translator/JavaTranslateVisitor.java  | 12 ++++++
 .../gremlin/process/traversal/GremlinLang.java     |  5 +++
 .../language/translator/GremlinTranslatorTest.java | 10 ++---
 .../gremlin/process/traversal/GremlinLangTest.java |  2 +
 .../python/gremlin_python/process/traversal.py     |  4 ++
 .../src/main/python/tests/driver/test_client.py    | 10 ++---
 .../main/python/tests/process/test_gremlin_lang.py |  4 ++
 .../GraphBinaryRemoteGraphComputerProvider.java    |  4 --
 .../process/traversal/step/map/PropertiesTest.java | 15 -------
 .../traversal/step/sideEffect/Neo4jGraphStep.java  |  4 +-
 11 files changed, 70 insertions(+), 47 deletions(-)


(tinkerpop) 01/01: GValue and Antlr Parsing Cache (#3157)

Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

colegreer pushed a commit to branch GValue-master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 7fbdff08c67a86d1a0a9715c0fbc79ae8e6f2901
Author: Cole Greer <11...@users.noreply.github.com>
AuthorDate: Tue Aug 19 18:40:30 2025 -0700

    GValue and Antlr Parsing Cache (#3157)
    
    This change introduces GValue, which is essentially a wrapper around a query parameter name and value. The primary goal of GValues is to persist the parameterized state of a traversal through strategy execution. This enables post-strategy Traversals to cached, and for parameterized values to remain mutable after a Traversal is retrieved from the cache.
    
    These changes introduce new "StepContract" interfaces and "GValue Placeholder" steps for parameterizable steps. A GValue placeholder is a non-executable placeholder step which may store its arguments as GValues. There is a new finalization strategy GValueReductionStrategy which is intended to be applied after a traversal has been stored in the cache which "reduces" all placeholder steps to their concrete non-parameterized form. The primary intention of this structure is to prevent GVa [...]
    
    Some optimization strategies such as EarlyLimitStrategy may alter a Traversal based on a specific argument value in such a way that is not generalizable for all substitutions of that parameter. To guard against this, each traversal is given a GValueManager which tracks if a certain variable has been "pinned". A traversal with a pinned variable is only valid for the current values of that variable, and does not allow for substitution of the pinned variable.
    
    Also included is an optional new cache in GremlinLangScriptEngine which can be used to save on antlr parsing time which is built using the GValue improvements.
    
    ---------
    
    Co-authored-by: Stephen Mallette <st...@amazon.com>
    Co-authored-by: xiazcy <xi...@gmail.com>
    Co-authored-by: andreachild <an...@improving.com>
---
 docs/src/reference/gremlin-variants.asciidoc       |  27 +
 docs/src/reference/the-traversal.asciidoc          |  31 +-
 gremlin-core/pom.xml                               |   4 +
 .../GremlinLangCustomizer.java}                    |  26 +-
 .../gremlin/jsr223/GremlinLangPlugin.java          |  71 ++
 .../gremlin/jsr223/GremlinLangScriptEngine.java    |  75 ++-
 .../gremlin/language/grammar/ArgumentVisitor.java  | 112 +---
 .../grammar/DefaultGremlinBaseVisitor.java         |  17 +-
 .../language/grammar/GenericLiteralVisitor.java    |   6 +-
 .../language/grammar/GremlinQueryParser.java       |   2 +-
 .../language/grammar/TraversalMethodVisitor.java   | 145 ++---
 .../grammar/TraversalSourceSelfMethodVisitor.java  |  13 +-
 .../grammar/TraversalSourceSpawnMethodVisitor.java |  26 +-
 .../translator/DotNetTranslateVisitor.java         |  28 +-
 .../translator/GroovyTranslateVisitor.java         |  37 +-
 .../translator/PythonTranslateVisitor.java         |   5 +
 .../strategy/decoration/VertexProgramStrategy.java |   2 +-
 .../remote/traversal/AbstractRemoteTraversal.java  |  11 +
 .../gremlin/process/traversal/GValueManager.java   | 211 ++++++
 .../tinkerpop/gremlin/process/traversal/P.java     | 224 ++++++-
 .../gremlin/process/traversal/SackFunctions.java   |   5 +-
 .../tinkerpop/gremlin/process/traversal/TextP.java |  96 ++-
 .../gremlin/process/traversal/Traversal.java       |  16 +-
 .../process/traversal/TraversalStrategies.java     |   5 +-
 .../process/traversal/TraversalStrategy.java       |  35 +-
 .../traversal/dsl/graph/GraphTraversal.java        | 602 +++++++++--------
 .../traversal/dsl/graph/GraphTraversalSource.java  |  89 ++-
 .../gremlin/process/traversal/dsl/graph/__.java    |  55 +-
 .../traversal/lambda/AbstractLambdaTraversal.java  |  15 +-
 .../traversal/lambda/GValueConstantTraversal.java  |  78 +++
 .../gremlin/process/traversal/step/GValue.java     | 236 ++++---
 .../process/traversal/step/GValueHolder.java       |  45 ++
 .../process/traversal/step/HasContainerHolder.java |  43 +-
 .../gremlin/process/traversal/step/Mutating.java   |   2 +-
 .../step/{Ranging.java => PropertyAdding.java}     |  16 +-
 .../gremlin/process/traversal/step/Ranging.java    |   4 +-
 .../process/traversal/step/TraversalParent.java    |   5 +
 .../process/traversal/step/filter/DropStep.java    |  14 -
 .../process/traversal/step/filter/HasStep.java     |   2 +-
 .../process/traversal/step/filter/IsStep.java      |   4 +-
 .../traversal/step/filter/IsStepContract.java}     |  28 +-
 .../filter/{IsStep.java => IsStepPlaceholder.java} |  55 +-
 .../traversal/step/filter/RangeGlobalStep.java     |  41 +-
 .../step/filter/RangeGlobalStepContract.java}      |  31 +-
 .../step/filter/RangeLocalStepContract.java}       |  31 +-
 .../step/filter/RangeStepPlaceholder.java          | 186 ++++++
 .../traversal/step/filter/TailGlobalStep.java      |  29 +-
 .../TailGlobalStepContract.java}                   |  13 +-
 .../step/filter/TailGlobalStepPlaceholder.java     | 102 +++
 .../TailLocalStepContract.java}                    |  13 +-
 .../step/filter/TailLocalStepPlaceholder.java      |  99 +++
 .../map/AbstractAddElementStepPlaceholder.java     | 302 +++++++++
 .../traversal/step/map/AddEdgeStartStep.java       |  86 ++-
 .../step/map/AddEdgeStartStepPlaceholder.java      | 162 +++++
 .../process/traversal/step/map/AddEdgeStep.java    | 129 ++--
 .../traversal/step/map/AddEdgeStepContract.java    |  92 +++
 .../traversal/step/map/AddEdgeStepPlaceholder.java | 158 +++++
 .../step/map/AddElementStepContract.java}          |  30 +-
 .../traversal/step/map/AddVertexStartStep.java     |  76 ++-
 .../step/map/AddVertexStartStepPlaceholder.java    |  73 +++
 .../process/traversal/step/map/AddVertexStep.java  |  68 +-
 .../AddVertexStepContract.java}                    |  20 +-
 .../step/map/AddVertexStepPlaceholder.java         |  73 +++
 .../process/traversal/step/map/CallStep.java       |  48 +-
 .../traversal/step/map/CallStepContract.java}      |  34 +-
 .../traversal/step/map/CallStepPlaceholder.java    | 262 ++++++++
 .../process/traversal/step/map/GraphStep.java      | 101 +--
 .../{Ranging.java => map/GraphStepContract.java}   |  22 +-
 .../traversal/step/map/GraphStepPlaceholder.java   | 162 +++++
 .../process/traversal/step/map/MergeEdgeStep.java  |  14 +-
 .../step/map/MergeEdgeStepPlaceholder.java         | 327 ++++++++++
 .../traversal/step/map/MergeElementStep.java       |  92 ++-
 .../traversal/step/map/MergeStepContract.java      |  56 ++
 .../traversal/step/map/MergeVertexStep.java        |  15 +-
 .../step/map/MergeVertexStepPlaceholder.java       | 313 +++++++++
 .../process/traversal/step/map/RangeLocalStep.java |  35 +-
 .../process/traversal/step/map/TailLocalStep.java  |  20 +-
 .../process/traversal/step/map/VertexStep.java     |  26 +-
 .../traversal/step/map/VertexStepContract.java}    |  38 +-
 ...{VertexStep.java => VertexStepPlaceholder.java} | 119 ++--
 .../traversal/step/sideEffect/AddPropertyStep.java |  47 +-
 .../step/sideEffect/AddPropertyStepContract.java   |  52 ++
 .../sideEffect/AddPropertyStepPlaceholder.java     | 224 +++++++
 .../process/traversal/step/util/GValueHelper.java  |  90 +++
 .../process/traversal/step/util/HasContainer.java  |   6 +-
 .../process/traversal/step/util/Parameters.java    |  19 +-
 .../strategy/decoration/ElementIdStrategy.java     |  34 +-
 .../strategy/decoration/EventStrategy.java         |  14 +-
 .../strategy/decoration/PartitionStrategy.java     |  66 +-
 .../strategy/decoration/SubgraphStrategy.java      |  43 +-
 .../finalization/GValueReductionStrategy.java      |  54 ++
 .../optimization/AdjacentToIncidentStrategy.java   |   8 +-
 .../strategy/optimization/CountStrategy.java       |   9 +-
 .../strategy/optimization/EarlyLimitStrategy.java  |  14 +-
 .../optimization/IncidentToAdjacentStrategy.java   |  17 +-
 .../optimization/InlineFilterStrategy.java         |  92 +--
 .../strategy/optimization/OrderLimitStrategy.java  |  11 +-
 .../optimization/PathProcessorStrategy.java        |   2 +-
 .../ReservedKeysVerificationStrategy.java          |  20 +-
 .../process/traversal/util/ConnectiveP.java        |  29 +-
 .../process/traversal/util/DefaultTraversal.java   |  18 +-
 .../process/traversal/util/EmptyTraversal.java     |  36 +-
 .../process/traversal/util/PureTraversal.java      |   4 +
 .../process/traversal/util/TraversalHelper.java    |  95 ++-
 .../gremlin/structure/io/gryo/GryoVersion.java     |  10 +-
 .../language/grammar/ArgumentVisitorTest.java      |  19 +-
 .../language/grammar/TraversalRootVisitorTest.java |  51 --
 .../language/translator/GremlinTranslatorTest.java |  28 +-
 .../gremlin/process/TraversalStrategiesTest.java   |   2 +-
 .../remote/EmbeddedRemoteConnectionTest.java       |   3 +-
 .../process/traversal/GValueManagerTest.java       | 309 +++++++++
 .../tinkerpop/gremlin/process/traversal/PTest.java | 102 ++-
 .../gremlin/process/traversal/TraversalTest.java   |   8 +
 .../traversal/dsl/graph/GraphTraversalTest.java    |   3 -
 .../process/traversal/step/GValueStepTest.java     |  75 +++
 .../gremlin/process/traversal/step/GValueTest.java | 376 ++++++-----
 .../process/traversal/step/filter/HasStepTest.java |  83 ++-
 .../process/traversal/step/filter/IsStepTest.java  |  61 +-
 .../traversal/step/filter/RangeGlobalStepTest.java |  70 +-
 .../traversal/step/filter/TailGlobalStepTest.java  |  52 +-
 .../traversal/step/map/AddEdgeStartStepTest.java   | 231 +++++++
 .../traversal/step/map/AddEdgeStepTest.java        | 274 +++++++-
 .../traversal/step/map/AddVertexStartStepTest.java | 180 +++++
 .../traversal/step/map/AddVertexStepTest.java      | 285 +++++++-
 .../process/traversal/step/map/CallStepTest.java   | 174 ++++-
 .../process/traversal/step/map/GraphStepTest.java  |  70 +-
 .../traversal/step/map/MergeEdgeStepTest.java      | 184 +++++-
 .../traversal/step/map/MergeVertexStepTest.java    | 199 +++++-
 .../traversal/step/map/RangeLocalStepTest.java     |  67 +-
 .../traversal/step/map/TailLocalStepTest.java      |  53 +-
 .../process/traversal/step/map/VertexStepTest.java | 104 ++-
 .../step/sideEffect/AddPropertyStepTest.java       | 108 ++-
 .../traversal/step/util/ParametersTest.java        |   4 +-
 .../traversal/strategy/GValueManagerVerifier.java  | 385 +++++++++++
 .../strategy/GValueManagerVerifierTest.java        | 240 +++++++
 .../decoration/ConnectiveStrategyTest.java         | 180 +++--
 .../decoration/ElementIdStrategyTraverseTest.java  |  16 +-
 .../strategy/decoration/PartitionStrategyTest.java | 184 +++++-
 .../strategy/decoration/SubgraphStrategyTest.java  | 100 +++
 .../AdjacentToIncidentStrategyTest.java            | 161 +++--
 .../ByModulatorOptimizationStrategyTest.java       |  14 +-
 .../strategy/optimization/CountStrategyTest.java   | 386 +++++++----
 .../optimization/EarlyLimitStrategyTest.java       | 330 ++++++++--
 .../optimization/FilterRankingStrategyTest.java    | 411 +++++++-----
 .../optimization/IdentityRemovalStrategyTest.java  |  14 +-
 .../IncidentToAdjacentStrategyTest.java            | 158 +++--
 .../optimization/InlineFilterStrategyTest.java     | 263 +++++---
 .../optimization/OrderLimitStrategyTest.java       | 106 ++-
 .../optimization/PathProcessorStrategyTest.java    | 201 ++++--
 .../optimization/RepeatUnrollStrategyTest.java     | 195 +++---
 .../gremlin/structure/util/GraphFactoryTest.java   |  10 +
 .../tinkerpop/gremlin/util/CollectionUtilTest.java |   2 +-
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 659 +++++++++----------
 gremlin-go/driver/client_test.go                   |  50 ++
 gremlin-go/driver/cucumber/cucumberSteps_test.go   |   2 +
 gremlin-go/driver/cucumber/gremlin.go              | 256 ++++----
 gremlin-groovy/pom.xml                             |   1 -
 .../gremlin-javascript/test/cucumber/gremlin.js    | 154 ++---
 gremlin-language/src/main/antlr4/Gremlin.g4        |  50 +-
 .../python/gremlin_python/process/traversal.py     |   4 +
 gremlin-python/src/main/python/radish/gremlin.py   | 156 ++---
 .../src/main/python/tests/driver/test_client.py    |  10 +-
 .../main/python/tests/process/test_gremlin_lang.py |   4 +
 .../GraphBinaryRemoteGraphComputerProvider.java    |   4 -
 .../gremlin/server/GremlinDriverIntegrateTest.java |  14 +-
 .../tinkerpop/gremlin/features/StepDefinition.java |   1 +
 .../process/traversal/step/map/PropertiesTest.java |  15 -
 .../gremlin/test/features/filter/All.feature       |  16 +-
 .../gremlin/test/features/filter/Any.feature       |  16 +-
 .../gremlin/test/features/filter/Coin.feature      |  17 -
 .../gremlin/test/features/filter/None.feature      |  20 +-
 .../gremlin/test/features/map/AsBool.feature       |   3 +-
 .../gremlin/test/features/map/AsDate.feature       |   9 +-
 .../gremlin/test/features/map/AsString.feature     |  17 +-
 .../gremlin/test/features/map/Combine.feature      |  37 +-
 .../gremlin/test/features/map/Conjoin.feature      |  12 -
 .../gremlin/test/features/map/Difference.feature   |  22 +-
 .../gremlin/test/features/map/Disjunct.feature     |  10 -
 .../gremlin/test/features/map/Intersect.feature    |  31 +-
 .../gremlin/test/features/map/LTrim.feature        |   6 +-
 .../gremlin/test/features/map/Merge.feature        |  30 +-
 .../gremlin/test/features/map/MergeEdge.feature    |  14 +-
 .../gremlin/test/features/map/MergeVertex.feature  |  52 +-
 .../gremlin/test/features/map/Product.feature      |  10 -
 .../gremlin/test/features/map/RTrim.feature        |   6 +-
 .../gremlin/test/features/map/Reverse.feature      |   3 +-
 .../gremlin/test/features/map/Split.feature        |   3 +-
 .../gremlin/test/features/map/Substring.feature    |   3 +-
 .../gremlin/test/features/map/ToLower.feature      |   6 +-
 .../test/features/semantics/Comparability.feature  |  44 --
 .../test/features/semantics/Orderability.feature   |  34 +-
 .../test/features/sideEffect/Inject.feature        |  73 +--
 neo4j-gremlin/pom.xml                              |   1 -
 .../traversal/step/sideEffect/Neo4jGraphStep.java  |   6 +-
 .../optimization/Neo4jGraphStepStrategy.java       |   5 +-
 .../gremlin/neo4j/Neo4jGraphFeatureTest.java       |   2 +-
 pom.xml                                            |   5 +
 .../traversal/step/sideEffect/TinkerGraphStep.java |  16 +-
 .../optimization/TinkerGraphStepStrategy.java      |   5 +-
 .../TinkerGraphGremlinLangScriptEngineTest.java    | 722 +++++++++++++++++++++
 .../process/TinkerGraphNoStrategyProvider.java     |   4 +-
 .../step/sideEffect/TinkerGraphStepTest.java       |  75 ++-
 .../tinkergraph/structure/TinkerGraphPlayTest.java |   6 +-
 203 files changed, 12359 insertions(+), 3928 deletions(-)

diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc
index 124a3f5df4..9d9dcad78a 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -892,6 +892,33 @@ g.V().has("name", GValue.ofString("name", "marko"));
 g.mergeV(GValue.ofMap("vertexPattern", Collections.singletonMap("name", "marko")));
 ----
 
+[[gremlin-java-gvalue]]
+=== GValue Parameterization
+
+A `GValue` is an encapsulation of a parameter name and value. The GValue class has a series of static methods to
+construct GValues of various types from a given parameter name and value. Some of the most common examples are listed
+below, see the
+link:++https://tinkerpop.apache.org/javadocs/x.y.z/full/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.html#method.summary++[Javadocs]
+for a complete listing.
+
+[source,java]
+----
+GValue<String> stringArg = GValue.ofString("name", "value");
+GValue<Integer> intArg = GValue.ofInteger("name", 1);
+GValue<Map> mapArg = GValue.ofMap("name", Collections.emptyMap());
+GValue<?> autoTypedArg = GValue.of("name", "value"); // GValue will attempt to automatically detect correct type
+----
+
+A <<traversal-parameterization,subset of gremlin steps>> are able to accept `GValues`. When constructing a
+`GraphTraversal` with such steps in Java, a GValue may be passed in the traversal to utilize a parameter in place of a
+literal.
+
+[source,java]
+----
+g.V().has("name", GValue.ofString("name", "marko"));
+g.mergeV(GValue.ofMap("vertexPattern", Collections.singletonMap("name", "marko")));
+----
+
 [[gremlin-java-lambda]]
 === The Lambda Solution
 
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 15b2b1279a..67976ba529 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -5438,39 +5438,34 @@ a comparatively slow script compilation, which makes parameterization essential
 
 |<<addedge-step,addE()>> | String edgeLabel
 |<<addvertex-step,addV()>> | String vertexLabel
-|<<vertex-steps,both()>> | String...edgeLabels
-|<<vertex-steps,bothE()>> | String...edgeLabels
+|<<vertex-steps,both()>> | String... edgeLabels
+|<<vertex-steps,bothE()>> | String... edgeLabels
 |<<call-step,call()>> | Map params
-|<<coin-step,coin()>> | double probability
-|<<combine-step,combine()>> | Object values
-|<<conjoin-step,conjoin()>> | String delimiter
-|<<constant-step,constant()>> | E e/value
-|<<difference-step,difference()>> | Object values
-|<<disjunct-step,disjunct()>> | Object values
 |<<from-step,from()>> | Vertex fromVertex
 |<<has-step,has()>> | String label
+|<<has-step,hasId()>> | Object id, Object... ids
 |<<has-step,hasLabel()>> | String label, String... labels
-|<<vertex-steps,in()>> | String...edgeLabels
-|<<vertex-steps,inE()>> | String...edgeLabels
-|<<intersect-step,intersect()>> | Object values
+|<<has-step,hasValue()>> | Object value, Object... values
+|<<vertex-steps,in()>> | String... edgeLabels
+|<<vertex-steps,inE()>> | String... edgeLabels
+|<<is-step,is()>> | Object value
 |<<limit-step,limit()>> | Long limit
-|<<merge-step,merge()>> | Object values
 |<<mergeedge-step,mergeE()>> | Map searchCreate
 |<<mergevertex-step,mergeV()>> | Map searchCreate
-|<<option-step,option()>> | M token, Map m
-|<<vertex-steps,out()>> | String...edgeLabels
-|<<vertex-steps,outE()>> | String...edgeLabels
-|<<product-step,product()>> | Object values
+|<<option-step,option()>> | Map m
+|<<vertex-steps,out()>> | String... edgeLabels
+|<<vertex-steps,outE()>> | String... edgeLabels
+|<<property-step,property()>> | Object value, Object... values
 |<<range-step,range()>> | Long low, Long high
 |<<skip-step,skip()>> | Long limit
 |<<tail-step,tail()>> | Long limit
 |<<to-step,to()>> | String... edgeLabels, Vertex toVertex
-|<<vertex-steps,toE()>> | String...edgeLabels
+|<<vertex-steps,toE()>> | String... edgeLabels
 |===
 
 *Additional References*
 
-<<gremlin-java-gvalue,Java>>, <<gremlin-python-gvalue,Python>>, <<parameterized-scripts,Server>>
+<<gremlin-java-gvalue,Java>>, <<parameterized-scripts,Server>>
 
 [[a-note-on-predicates]]
 == A Note on Predicates
diff --git a/gremlin-core/pom.xml b/gremlin-core/pom.xml
index a8b73d1c1e..7d547d0e5f 100644
--- a/gremlin-core/pom.xml
+++ b/gremlin-core/pom.xml
@@ -83,6 +83,10 @@ limitations under the License.
             <artifactId>exp4j</artifactId>
             <version>${exp4j.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
         <!-- TESTING -->
         <dependency>
             <groupId>junit</groupId>
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangCustomizer.java
similarity index 56%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangCustomizer.java
index 67c3d1e7e4..4abbb31b77 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangCustomizer.java
@@ -16,22 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.tinkerpop.gremlin.process.traversal.step;
+package org.apache.tinkerpop.gremlin.jsr223;
 
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
-
-import java.util.List;
+import com.github.benmanes.caffeine.cache.Caffeine;
 
 /**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * Designed to provide basic configuration options to the {@link GremlinLangScriptEngine}.
  */
-public interface HasContainerHolder {
+public class GremlinLangCustomizer implements Customizer {
+
+    private final boolean cacheEnabled;
+    private final Caffeine cacheBuilder;
 
-    public List<HasContainer> getHasContainers();
+    public GremlinLangCustomizer(final boolean cacheEnabled, final Caffeine cacheBuilder) {
+        this.cacheEnabled = cacheEnabled;
+        this.cacheBuilder = cacheBuilder;
+    }
 
-    public void addHasContainer(final HasContainer hasContainer);
+    public boolean isCacheEnabled() {
+        return cacheEnabled;
+    }
 
-    public default void removeHasContainer(final HasContainer hasContainer) {
-        throw new UnsupportedOperationException("The holder does not support container removal: " + this.getClass().getCanonicalName());
+    public Caffeine getCacheMaker() {
+        return cacheBuilder;
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangPlugin.java
new file mode 100644
index 0000000000..a2d925a8ce
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangPlugin.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.jsr223;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.CaffeineSpec;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Exposes {@link Customizer} implementations for configuring the {@link GremlinLangScriptEngine}.
+ */
+public class GremlinLangPlugin extends AbstractGremlinPlugin {
+    private static final String NAME = "tinkerpop.gremlin-language";
+    private static final GremlinLangScriptEngineFactory factory = new GremlinLangScriptEngineFactory();
+    private static final Set<String> APPLIES_TO = Collections.singleton(factory.getEngineName());
+
+    private GremlinLangPlugin(final Builder builder) {
+        super(NAME, APPLIES_TO,
+                new GremlinLangCustomizer(builder.cacheEnabled,
+                        builder.caffeineSpec != null ? Caffeine.from(builder.caffeineSpec) : Caffeine.newBuilder()));
+    }
+
+    /**
+     * Builds a set of static bindings.
+     */
+    public static GremlinLangPlugin.Builder build() {
+        return new GremlinLangPlugin.Builder();
+    }
+
+    public static class Builder {
+        private boolean cacheEnabled = false;
+        private CaffeineSpec caffeineSpec = null;
+
+        public Builder cacheEnabled(final boolean cacheEnabled) {
+            this.cacheEnabled = cacheEnabled;
+            return this;
+        }
+
+        /**
+         * Provide a Caffeine spec formatted cache definition to configure the cache. For example,
+         * "maximumWeight=1000, expireAfterWrite=10m", see
+         * <a href="https://github.com/ben-manes/caffeine/wiki/Specification">Caffeine Documatation</a> for details.
+         */
+        public Builder caffeine(final String spec) {
+            this.caffeineSpec = CaffeineSpec.parse(spec);
+            return this;
+        }
+
+        public GremlinLangPlugin create() {
+            return new GremlinLangPlugin(this);
+        }
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
index 5528b280b1..376f731f49 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
@@ -18,11 +18,16 @@
  */
 package org.apache.tinkerpop.gremlin.jsr223;
 
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
 import org.apache.tinkerpop.gremlin.language.grammar.GremlinAntlrToJava;
 import org.apache.tinkerpop.gremlin.language.grammar.GremlinQueryParser;
 import org.apache.tinkerpop.gremlin.language.grammar.VariableResolver;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 
 import javax.script.AbstractScriptEngine;
 import javax.script.Bindings;
@@ -35,6 +40,8 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
 /**
@@ -50,12 +57,28 @@ import java.util.function.Function;
  * implementation represents the first step to changes in what it means to have a {@link GremlinScriptEngine}. In some
  * sense, there is question why a {@link GremlinScriptEngine} approach is necessary at all except for easily plugging
  * into the existing internals of Gremlin Server or more specifically the {@code GremlinExecutor}.
+ * <p/>
+ * The engine includes a cache that can help spare repeated parsing of the same traversal. It is keyed to each
+ * configuration of the {@link GraphTraversalSource} that initially requested its execution via the bindings given to
+ * the {@code eval} method. Each cache is keyed on the original Gremlin string and holds the initialized parsed
+ * traversal for its value. If that Gremlin string is matched again in the future, it will use the one from the cache
+ * rather than parse again via the ANTLR grammar. In addition, if {@link GValue} instances were used in formation of
+ * the traversal, it will substitute in new bindings given with the {@code eval}.
  */
 public class GremlinLangScriptEngine extends AbstractScriptEngine implements GremlinScriptEngine {
     private volatile GremlinScriptEngineFactory factory;
 
     private final Function<Map<String, Object>, VariableResolver> variableResolverMaker;
 
+    private final Map<GraphTraversalSource, Cache<String, Traversal.Admin<?,?>>> traversalCaches = new ConcurrentHashMap<>();
+
+    /**
+     * Determines if the traversal cache is enabled or not. This is {@code false} by default.
+     */
+    private final boolean cacheEnabled;
+
+    private final Caffeine<String, Traversal<?,?>> caffeine;
+
     /**
      * Creates a new instance using no {@link Customizer}.
      */
@@ -68,11 +91,24 @@ public class GremlinLangScriptEngine extends AbstractScriptEngine implements Gre
 
         // this ScriptEngine really only supports the VariableResolverCustomizer to configure the VariableResolver
         // and can't configure it more than once. first one wins
-        final Optional<Customizer> opt = listOfCustomizers.stream().filter(c -> c instanceof VariableResolverCustomizer).findFirst();
+        final Optional<Customizer> opt = listOfCustomizers.stream().
+                filter(c -> c instanceof VariableResolverCustomizer).findFirst();
         variableResolverMaker = opt.isPresent() ?
                 ((VariableResolverCustomizer) opt.get()).getVariableResolverMaker() :
                 VariableResolver.DirectVariableResolver::new;
 
+        // cache customization
+        final Optional<GremlinLangCustomizer> gremlinLangCustomizer = listOfCustomizers.stream().
+                filter(c -> c instanceof GremlinLangCustomizer).
+                map(c -> (GremlinLangCustomizer) c).findFirst();
+        if (gremlinLangCustomizer.isPresent()) {
+            final GremlinLangCustomizer customizer = gremlinLangCustomizer.get();
+            cacheEnabled = customizer.isCacheEnabled();
+            caffeine = customizer.getCacheMaker();
+        } else {
+            cacheEnabled = false;
+            caffeine = null;
+        }
     }
 
     @Override
@@ -97,12 +133,47 @@ public class GremlinLangScriptEngine extends AbstractScriptEngine implements Gre
         if (!(o instanceof GraphTraversalSource))
             throw new IllegalArgumentException("g is of type " + o.getClass().getSimpleName() + " and is not an instance of TraversalSource");
 
+        final GraphTraversalSource g = (GraphTraversalSource) o;
         final Map<String, Object> m = context.getBindings(ScriptContext.ENGINE_SCOPE);
+
+        Cache<String, Traversal.Admin<?, ?>> traversalCache = null;
+        if (cacheEnabled) {
+            // find the property cache for the g that was passed in so that we have the right Graph instance/context
+            traversalCache = traversalCaches.computeIfAbsent(g, k -> caffeine.build());
+
+            // if found in cache, clone the traversal, apply new bindings and then return the clone
+            final Traversal cachedTraversal = traversalCache.getIfPresent(script);
+            if (cachedTraversal != null) {
+                final Traversal.Admin<?, ?> clonedTraversal = cachedTraversal.asAdmin().clone();
+                final GValueManager manager = clonedTraversal.getGValueManager();
+                final Set<String> variables = manager.getVariableNames();
+
+                // every variable the traversal has should match a binding
+                for (String variable : variables) {
+                    if (!m.containsKey(variable)) {
+                        throw new IllegalArgumentException(variable + " binding is not found");
+                    }
+
+                    manager.updateVariable(variable, m.get(variable));
+                }
+
+                return clonedTraversal;
+            }
+        }
+
+        // if not in cache or cache not enabled, parse the script
         final GremlinAntlrToJava antlr = new GremlinAntlrToJava((GraphTraversalSource) o,
                 variableResolverMaker.apply(m));
 
         try {
-            return GremlinQueryParser.parse(script, antlr);
+            // parse the script
+            final Traversal.Admin<?,?> traversal = (Traversal.Admin) GremlinQueryParser.parse(script, antlr);
+
+            // add the traversal to the cache in its executable form - note that the cache is really just sparing us
+            // script parsing
+            if (cacheEnabled) traversalCache.put(script, traversal.clone());
+
+            return traversal;
         } catch (Exception ex) {
             throw new ScriptException(ex);
         }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java
index cf307dc005..eafcfafb45 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java
@@ -20,11 +20,6 @@ package org.apache.tinkerpop.gremlin.language.grammar;
 
 import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.structure.Vertex;
-import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
-
-import java.lang.reflect.Array;
-import java.time.OffsetDateTime;
 import java.util.Map;
 import java.util.Objects;
 
@@ -39,61 +34,12 @@ public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> {
         this.antlr = antlr;
     }
 
-    /**
-     * Wrapper to visit function for boolean.
-     */
-    public boolean parseBoolean(final GremlinParser.BooleanArgumentContext ctx) {
-        return (boolean) visitBooleanArgument(ctx);
-    }
-
-    /**
-     * Wrapper to visit function for integer types.
-     */
-    public Number parseNumber(final GremlinParser.IntegerArgumentContext ctx) {
-        return (Number) visitIntegerArgument(ctx);
-    }
-
-    /**
-     * Wrapper to visit function for float types.
-     */
-    public Number parseNumber(final GremlinParser.FloatArgumentContext ctx) {
-        return (Number) visitFloatArgument(ctx);
-    }
-
-    /**
-     * Wrapper to visit function for string types.
-     */
-    public String parseString(final GremlinParser.StringArgumentContext ctx) {
-        return (String) visitStringArgument(ctx);
-    }
-
-    /**
-     * Wrapper to visit function for Date type.
-     */
-    public OffsetDateTime parseDate(final GremlinParser.DateArgumentContext ctx) {
-        return (OffsetDateTime) visitDateArgument(ctx);
-    }
-
-    /**
-     * Wrapper for visit function for object types.
-     */
-    public Object parseObject(final GremlinParser.GenericArgumentContext ctx) {
-        return visitGenericArgument(ctx);
-    }
-
-    /**
-     * Wrapper for visit function for {@code Map} types.
-     */
-    public Map parseMap(final GremlinParser.GenericMapArgumentContext ctx) {
-        return (Map) visitGenericMapArgument(ctx);
-    }
-
     /**
      * Wrapper for visit function for {@code Map} types.
      */
     public Map parseMap(final GremlinParser.GenericMapNullableArgumentContext ctx) {
-        Object literalOrVar = visitGenericMapNullableArgument(ctx);
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP)) {
+        final Object literalOrVar = visitGenericMapNullableArgument(ctx);
+        if (GValue.valueInstanceOf(literalOrVar, Map.class)) {
             return ((GValue<Map>) literalOrVar).get();
         } else {
             return (Map) literalOrVar;
@@ -114,12 +60,26 @@ public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> {
                 .toArray(Object[]::new);
     }
 
+    /**
+     * Parse a string argument varargs, and return an string array
+     */
+    public GValue<String>[] parseStringVarargs(final GremlinParser.StringNullableArgumentVarargsContext varargsContext) {
+        if (varargsContext == null || varargsContext.stringNullableArgument() == null) {
+            return new GValue[0];
+        }
+        return varargsContext.stringNullableArgument()
+                .stream()
+                .filter(Objects::nonNull)
+                .map(antlr.argumentVisitor::parseString)
+                .toArray(GValue[]::new);
+    }
+
     /**
      * Wrapper to visit function for string types.
      */
     public GValue<String> parseString(final GremlinParser.StringNullableArgumentContext ctx) {
-        Object literalOrVar = visitStringNullableArgument((ctx));
-        if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+        final Object literalOrVar = visitStringNullableArgument((ctx));
+        if (GValue.valueInstanceOf(literalOrVar, String.class)) {
             return (GValue<String>) literalOrVar;
         } else {
             return GValue.ofString(null, (String) literalOrVar);
@@ -135,17 +95,17 @@ public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> {
         if (ctx.integerLiteral() != null) {
             return antlr.genericVisitor.parseIntegral(ctx.integerLiteral()).longValue();
         } else {
-            Object var = visitVariable(ctx.variable());
+            final Object var = visitVariable(ctx.variable());
             if (var instanceof Number) {
                 return ((Number) var).longValue();
             }
-            if (GValue.valueInstanceOf(var, GType.LONG)) {
+            if (GValue.valueInstanceOf(var, Long.class)) {
                 return var;
-            } else if (GValue.valueInstanceOf(var, GType.INTEGER)) {
+            } else if (GValue.valueInstanceOf(var, Integer.class)) {
                 return GValue.ofLong(((GValue<Integer>) var).getName(), ((GValue<Integer>) var).get().longValue());
-            } else if (GValue.valueInstanceOf(var, GType.SHORT)) {
+            } else if (GValue.valueInstanceOf(var, Short.class)) {
                 return GValue.ofLong(((GValue<Short>) var).getName(), ((GValue<Short>) var).get().longValue());
-            } else if (GValue.valueInstanceOf(var, GType.BYTE)) {
+            } else if (GValue.valueInstanceOf(var, Byte.class)) {
                 return GValue.ofLong(((GValue<Byte>) var).getName(), ((GValue<Byte>) var).get().longValue());
             } else {
                 throw new GremlinParserException(String.format("Expected variable [%s] to resolve to an integer type, instead found: %s", ctx.variable().Identifier().getSymbol(), var.getClass().getName()));
@@ -198,6 +158,18 @@ public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> {
         }
     }
 
+    @Override
+    public Object visitStringNullableArgumentVarargs(final GremlinParser.StringNullableArgumentVarargsContext ctx) {
+        if (ctx == null || ctx.stringNullableArgument() == null) {
+            return new Object[0];
+        }
+        return ctx.stringNullableArgument()
+                .stream()
+                .filter(Objects::nonNull)
+                .map(antlr.argumentVisitor::visitStringNullableArgument)
+                .toArray();
+    }
+
     @Override
     public Object visitDateArgument(final GremlinParser.DateArgumentContext ctx) {
         if (ctx.dateLiteral() != null) {
@@ -234,20 +206,6 @@ public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> {
         }
     }
 
-    /**
-     * Parse a string literal varargs, and return a string array
-     */
-    public GValue<String>[] parseStringVarargs(final GremlinParser.StringNullableArgumentVarargsContext varargsArgumentContext) {
-        if (varargsArgumentContext == null || varargsArgumentContext.stringNullableArgument() == null) {
-            return new GValue[0];
-        }
-        return varargsArgumentContext.stringNullableArgument()
-                .stream()
-                .filter(Objects::nonNull)
-                .map(antlr.argumentVisitor::parseString)
-                .toArray(GValue[]::new);
-    }
-
     @Override
     public Object visitVariable(final GremlinParser.VariableContext ctx) {
         return resolver.apply(ctx.getText(), ctx);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
index ce317876ac..9859d8f2a2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
@@ -414,14 +414,11 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override public T visitTraversalMethod_from_GenricArgument(GremlinParser.TraversalMethod_from_GenricArgumentContext ctx) { return null; }
+	@Override public T visitTraversalMethod_from_GenricArgument(final GremlinParser.TraversalMethod_from_GenricArgumentContext ctx) { return null; }
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override
-	public T visitTraversalMethod_from_Traversal(GremlinParser.TraversalMethod_from_TraversalContext ctx) {
-		return null;
-	}
+	@Override public T visitTraversalMethod_from_Traversal(final GremlinParser.TraversalMethod_from_TraversalContext ctx) { notImplemented(ctx); return null; }
 	/**
 	 * {@inheritDoc}
 	 */
@@ -861,11 +858,11 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override public T visitTraversalMethod_to_Traversal(final GremlinParser.TraversalMethod_to_TraversalContext ctx) { notImplemented(ctx); return null; }
+	@Override public T visitTraversalMethod_to_GenricArgument(final GremlinParser.TraversalMethod_to_GenricArgumentContext ctx) { notImplemented(ctx); return null; }
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override public T visitTraversalMethod_to_GenricArgument(final GremlinParser.TraversalMethod_to_GenricArgumentContext ctx) { notImplemented(ctx); return null; }
+	@Override public T visitTraversalMethod_to_Traversal(final GremlinParser.TraversalMethod_to_TraversalContext ctx) { notImplemented(ctx); return null; }
 	/**
 	 * {@inheritDoc}
 	 */
@@ -969,7 +966,7 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override public T visitTraversalMethod_format_String(GremlinParser.TraversalMethod_format_StringContext ctx) { notImplemented(ctx); return null; }
+	@Override public T visitTraversalMethod_format_String(final GremlinParser.TraversalMethod_format_StringContext ctx) { notImplemented(ctx); return null; }
 	/**
 	 * {@inheritDoc}
 	 */
@@ -1129,7 +1126,7 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override public T visitTraversalDT(GremlinParser.TraversalDTContext ctx) { notImplemented(ctx); return null; }
+	@Override public T visitTraversalDT(final GremlinParser.TraversalDTContext ctx) { notImplemented(ctx); return null; }
 	/**
 	 * {@inheritDoc}
 	 */
@@ -1633,7 +1630,7 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override public T visitGenericArgumentVarargs(GremlinParser.GenericArgumentVarargsContext ctx) { notImplemented(ctx); return null; }
+	@Override public T visitGenericArgumentVarargs(final GremlinParser.GenericArgumentVarargsContext ctx) { notImplemented(ctx); return null; }
 	/**
 	 * {@inheritDoc}
 	 */
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
index 6809c7b43d..ec6a31a994 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
@@ -150,7 +150,7 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> {
         return varargsContext.stringNullableLiteral()
                 .stream()
                 .filter(Objects::nonNull)
-                .map(this::parseString)
+                .map(antlr.genericVisitor::parseString)
                 .toArray(String[]::new);
     }
 
@@ -503,9 +503,9 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitDateLiteral(final GremlinParser.DateLiteralContext ctx) {
-        if (ctx.stringArgument() == null)
+        if (ctx.stringLiteral() == null)
             return DatetimeHelper.datetime();
-        return DatetimeHelper.parse((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument()));
+        return DatetimeHelper.parse((String) antlr.genericVisitor.visitStringLiteral(ctx.stringLiteral()));
     }
 
     /**
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParser.java
index 4efb5fcf69..f9a5a15e55 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParser.java
@@ -88,7 +88,7 @@ public class GremlinQueryParser {
             // We just log ex here for debugging purposes.
             log.debug("Converting a java.lang.ClassCastException to GremlinParserException," +
                     " assuming that it indicates a semantic parse error.", ex);
-            throw new GremlinParserException("Failed to interpret Gremlin query: " + ex.getMessage());
+            throw new GremlinParserException("Failed to interpret Gremlin query: " + ex.getMessage(), ex);
         }
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
index 4ba9477f5d..b761c45951 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
@@ -30,7 +30,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.Column;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.T;
@@ -93,7 +92,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_addV_String(final GremlinParser.TraversalMethod_addV_StringContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(ctx.stringArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+        if (GValue.valueInstanceOf(literalOrVar, String.class)) {
             return this.graphTraversal.addV((GValue<String>) literalOrVar);
         } else {
             return this.graphTraversal.addV((String) literalOrVar);
@@ -106,7 +105,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_mergeV_Map(final GremlinParser.TraversalMethod_mergeV_MapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapNullableArgument(ctx.genericMapNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return graphTraversal.mergeV((GValue<Map<Object, Object>>) literalOrVar);
         else
             return graphTraversal.mergeV((Map) literalOrVar);
@@ -142,9 +141,9 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_mergeE_Map(final GremlinParser.TraversalMethod_mergeE_MapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapNullableArgument(ctx.genericMapNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return graphTraversal.mergeE((GValue<Map<Object, Object>>) literalOrVar);
-        else if (GValue.valueInstanceOf(literalOrVar, GType.UNKNOWN) && ((GValue) literalOrVar).get() == null)
+        else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).get() == null)
             return graphTraversal.mergeE(GValue.ofMap(((GValue) literalOrVar).getName(), null));
         else
             return graphTraversal.mergeE((Map) literalOrVar);
@@ -180,7 +179,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_addE_String(final GremlinParser.TraversalMethod_addE_StringContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(ctx.stringArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+        if (GValue.valueInstanceOf(literalOrVar, String.class)) {
             return this.graphTraversal.addE((GValue<String>) literalOrVar);
         } else {
             return this.graphTraversal.addE((String) literalOrVar);
@@ -484,11 +483,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_combine_Object(final GremlinParser.TraversalMethod_combine_ObjectContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
-        if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
-            return graphTraversal.combine((GValue<Object>) literalOrVar);
-        else
-            return graphTraversal.combine(literalOrVar);
+        return graphTraversal.combine(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -496,12 +491,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_coin(final GremlinParser.TraversalMethod_coinContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitFloatArgument(ctx.floatArgument());
-        if (GValue.valueInstanceOfNumeric(literalOrVar))
-            return graphTraversal.coin((GValue<Double>) literalOrVar);
-        else
-            return graphTraversal.coin(((Number) literalOrVar).doubleValue());
-
+        return graphTraversal.coin(((Number) antlr.genericVisitor.visitFloatLiteral(ctx.floatLiteral())).doubleValue());
     }
 
     /**
@@ -509,13 +499,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_conjoin_String(final GremlinParser.TraversalMethod_conjoin_StringContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(ctx.stringArgument());
-        if (literalOrVar instanceof String)
-            return graphTraversal.conjoin((String) literalOrVar);
-        else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.STRING)
-            return graphTraversal.conjoin((GValue<String>) literalOrVar);
-        else
-            throw new IllegalArgumentException("conjoin argument must be a string");
+        return graphTraversal.conjoin(antlr.genericVisitor.parseString(ctx.stringLiteral()));
     }
 
     /**
@@ -524,7 +508,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_constant(final GremlinParser.TraversalMethod_constantContext ctx) {
         return graphTraversal
-                .constant(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
+                .constant(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -574,11 +558,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_difference_Object(final GremlinParser.TraversalMethod_difference_ObjectContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
-        if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
-            return graphTraversal.difference((GValue<Object>) literalOrVar);
-        else
-            return graphTraversal.difference(literalOrVar);
+        return graphTraversal.difference(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -594,11 +574,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_disjunct_Object(final GremlinParser.TraversalMethod_disjunct_ObjectContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
-        if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
-            return graphTraversal.disjunct((GValue<Object>) literalOrVar);
-        else
-            return graphTraversal.disjunct(literalOrVar);
+        return graphTraversal.disjunct(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -687,7 +663,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_fold_Object_BiFunction(final GremlinParser.TraversalMethod_fold_Object_BiFunctionContext ctx) {
         return graphTraversal.fold(
-                antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()),
+                antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()),
                 (BiFunction) TraversalEnumParser.parseTraversalEnumFromContext(Operator.class, ctx.getChild(4)));
     }
 
@@ -792,24 +768,22 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     public GraphTraversal visitTraversalMethod_hasLabel_String_String(final GremlinParser.TraversalMethod_hasLabel_String_StringContext ctx) {
         if (ctx.getChildCount() == 4) {
             final Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument());
-            if (GValue.valueInstanceOf(literalOrVar, GType.STRING))
+            if (GValue.valueInstanceOf(literalOrVar, String.class))
                 return graphTraversal.hasLabel((GValue) literalOrVar);
             else
                 return graphTraversal.hasLabel((String) literalOrVar);
         } else {
             Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument());
-            Object[] literalOrVars = antlr.argumentVisitor.parseStringVarargs(ctx.stringNullableArgumentVarargs());
-
-            if (null == literalOrVar) literalOrVar = GValue.ofString(null, null);
+            Object[] literalOrVars = (Object[]) antlr.argumentVisitor.visitStringNullableArgumentVarargs(ctx.stringNullableArgumentVarargs());
 
             // if any are GValue then they all need to be GValue to call hasLabel
             if (literalOrVar instanceof GValue || Arrays.stream(literalOrVars).anyMatch(lov -> lov instanceof GValue)) {
-                literalOrVar = GValue.ensureGValue(literalOrVar);
+                literalOrVar = ArgumentVisitor.asGValue(literalOrVar);
                 literalOrVars = GValue.ensureGValues(literalOrVars);
             }
 
             // since we normalized above to gvalue or literal we can just test the first arg for gvalue-ness
-            if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+            if (GValue.valueInstanceOf(literalOrVar, String.class)) {
                 final GValue[] gvalueLiteralOrVars = literalOrVars == null ? null : Arrays.stream(literalOrVars).map(o -> (GValue) o).toArray(GValue[]::new);
                 return graphTraversal.hasLabel((GValue) literalOrVar, (GValue[]) gvalueLiteralOrVars);
             } else {
@@ -877,7 +851,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_has_String_String_Object(final GremlinParser.TraversalMethod_has_String_String_ObjectContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+        if (GValue.valueInstanceOf(literalOrVar, String.class)) {
             return graphTraversal.has((GValue) literalOrVar,
                     antlr.genericVisitor.parseString(ctx.stringNullableLiteral()),
                     antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
@@ -894,7 +868,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_has_String_String_P(final GremlinParser.TraversalMethod_has_String_String_PContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+        if (GValue.valueInstanceOf(literalOrVar, String.class)) {
             return graphTraversal.has((GValue) literalOrVar,
                     antlr.genericVisitor.parseString(ctx.stringNullableLiteral()),
                     antlr.traversalPredicateVisitor.visitTraversalPredicate(ctx.traversalPredicate()));
@@ -978,11 +952,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_intersect_Object(final GremlinParser.TraversalMethod_intersect_ObjectContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
-        if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
-            return graphTraversal.intersect((GValue<Object>) literalOrVar);
-        else
-            return graphTraversal.intersect(literalOrVar);
+        return graphTraversal.intersect(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -998,7 +968,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_inject(final GremlinParser.TraversalMethod_injectContext ctx) {
-        return graphTraversal.inject(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()));
+        return graphTraversal.inject(antlr.genericVisitor.parseObjectVarargs(ctx.genericLiteralVarargs()));
     }
 
     @Override
@@ -1044,7 +1014,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_limit_Scope_long(final GremlinParser.TraversalMethod_limit_Scope_longContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.parseLong(ctx.integerArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.LONG)) {
+        if (GValue.valueInstanceOf(literalOrVar, Long.class)) {
             return graphTraversal.limit(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
                     (GValue<Long>) literalOrVar);
         } else {
@@ -1059,7 +1029,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_limit_long(final GremlinParser.TraversalMethod_limit_longContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.parseLong(ctx.integerArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.LONG)) {
+        if (GValue.valueInstanceOf(literalOrVar, Long.class)) {
             return graphTraversal.limit((GValue<Long>) literalOrVar);
         } else {
             return graphTraversal.limit((Long) literalOrVar);
@@ -1129,7 +1099,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
         } else if (ctx.ioOptionsValues() != null) {
             o = WithOptionsVisitor.instance().visitIoOptionsValues(ctx.ioOptionsValues());
         } else {
-            o = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
+            o = antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral());
         }
         return graphTraversal.with(k, o);
     }
@@ -1193,11 +1163,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_merge_Object(final GremlinParser.TraversalMethod_merge_ObjectContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
-        if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
-            return graphTraversal.merge((GValue<Object>) literalOrVar);
-        else
-            return graphTraversal.merge(literalOrVar);
+        return graphTraversal.merge(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -1258,7 +1224,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_option_Merge_Map(final GremlinParser.TraversalMethod_option_Merge_MapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapNullableArgument(ctx.genericMapNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return graphTraversal.option(TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge()), (GValue<Map>) literalOrVar);
         else
             return graphTraversal.option(TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge()), (Map) literalOrVar);
@@ -1373,7 +1339,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_pageRank_double(final GremlinParser.TraversalMethod_pageRank_doubleContext ctx) {
-        return graphTraversal.pageRank(((Number) antlr.argumentVisitor.visitFloatArgument(ctx.floatArgument())).doubleValue());
+        return graphTraversal.pageRank(((Number) antlr.genericVisitor.visitFloatLiteral(ctx.floatLiteral())).doubleValue());
     }
 
     /**
@@ -1397,11 +1363,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_product_Object(final GremlinParser.TraversalMethod_product_ObjectContext ctx) {
-        final Object literalOrVar = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument());
-        if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
-            return graphTraversal.product((GValue<Object>) literalOrVar);
-        else
-            return graphTraversal.product(literalOrVar);
+        return graphTraversal.product(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
     }
 
     /**
@@ -1463,8 +1425,8 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_property_Cardinality_Object_Object_Object(final GremlinParser.TraversalMethod_property_Cardinality_Object_Object_ObjectContext ctx) {
         return graphTraversal.property(TraversalEnumParser.parseTraversalEnumFromContext(Cardinality.class, ctx.traversalCardinality()),
-                antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument(0)),
-                antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument(1)),
+                antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()),
+                antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()),
                 antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()));
     }
 
@@ -1474,11 +1436,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_property_Object_Object_Object(final GremlinParser.TraversalMethod_property_Object_Object_ObjectContext ctx) {
         if (ctx.getChildCount() == 6) {
-            return graphTraversal.property(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument(0)),
-                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument(1)));
+            return graphTraversal.property(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()),
+                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
         } else {
-            return graphTraversal.property(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument(0)),
-                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument(1)),
+            return graphTraversal.property(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()),
+                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()),
                     antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()));
         }
     }
@@ -1507,11 +1469,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     public GraphTraversal visitTraversalMethod_range_Scope_long_long(final GremlinParser.TraversalMethod_range_Scope_long_longContext ctx) {
         Object low = antlr.argumentVisitor.parseLong(ctx.integerArgument(0));
         Object high = antlr.argumentVisitor.parseLong(ctx.integerArgument(1));
-        if (GValue.valueInstanceOf(low, GType.LONG) || GValue.valueInstanceOf(high, GType.LONG)) {
-            if (!GValue.valueInstanceOf(low, GType.LONG)) {
+        if (GValue.valueInstanceOf(low, Long.class) || GValue.valueInstanceOf(high, Long.class)) {
+            if (!GValue.valueInstanceOf(low, Long.class)) {
                 low = GValue.ofLong(null, (Long) low);
             }
-            if (!GValue.valueInstanceOf(high, GType.LONG)) {
+            if (!GValue.valueInstanceOf(high, Long.class)) {
                 high = GValue.ofLong(null, (Long) high);
             }
             return graphTraversal.range(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
@@ -1529,11 +1491,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     public GraphTraversal visitTraversalMethod_range_long_long(final GremlinParser.TraversalMethod_range_long_longContext ctx) {
         Object low = antlr.argumentVisitor.parseLong(ctx.integerArgument(0));
         Object high = antlr.argumentVisitor.parseLong(ctx.integerArgument(1));
-        if (GValue.valueInstanceOf(low, GType.LONG) || GValue.valueInstanceOf(high, GType.LONG)) {
-            if (!GValue.valueInstanceOf(low, GType.LONG)) {
+        if (GValue.valueInstanceOf(low, Long.class) || GValue.valueInstanceOf(high, Long.class)) {
+            if (!GValue.valueInstanceOf(low, Long.class)) {
                 low = GValue.ofLong(null, (Long) low);
             }
-            if (!GValue.valueInstanceOf(high, GType.LONG)) {
+            if (!GValue.valueInstanceOf(high, Long.class)) {
                 high = GValue.ofLong(null, (Long) high);
             }
             return graphTraversal.range((GValue<Long>) low, (GValue<Long>) high);
@@ -1563,8 +1525,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_sample_Scope_int(final GremlinParser.TraversalMethod_sample_Scope_intContext ctx) {
-        return graphTraversal.sample(
-                TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
+        return graphTraversal.sample(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
                 antlr.genericVisitor.parseIntegral(ctx.integerLiteral()).intValue());
     }
 
@@ -1589,8 +1550,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_select_Pop_String(final GremlinParser.TraversalMethod_select_Pop_StringContext ctx) {
-        return graphTraversal.select(
-                TraversalEnumParser.parseTraversalEnumFromContext(Pop.class, ctx.traversalPop()),
+        return graphTraversal.select(TraversalEnumParser.parseTraversalEnumFromContext(Pop.class, ctx.traversalPop()),
                 antlr.genericVisitor.parseString(ctx.stringLiteral()));
     }
 
@@ -1599,8 +1559,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_select_Pop_String_String_String(final GremlinParser.TraversalMethod_select_Pop_String_String_StringContext ctx) {
-        return graphTraversal.select(
-                TraversalEnumParser.parseTraversalEnumFromContext(Pop.class, ctx.traversalPop()),
+        return graphTraversal.select(TraversalEnumParser.parseTraversalEnumFromContext(Pop.class, ctx.traversalPop()),
                 antlr.genericVisitor.parseString(ctx.stringLiteral(0)),
                 antlr.genericVisitor.parseString(ctx.stringLiteral(1)),
                 antlr.genericVisitor.parseStringVarargs(ctx.stringNullableLiteralVarargs()));
@@ -1658,7 +1617,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_skip_Scope_long(final GremlinParser.TraversalMethod_skip_Scope_longContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.parseLong(ctx.integerArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.LONG)) {
+        if (GValue.valueInstanceOf(literalOrVar, Long.class)) {
             return graphTraversal.skip(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
                     (GValue<Long>) literalOrVar);
         } else {
@@ -1673,7 +1632,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_skip_long(final GremlinParser.TraversalMethod_skip_longContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.parseLong(ctx.integerArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.LONG)) {
+        if (GValue.valueInstanceOf(literalOrVar, Long.class)) {
             return graphTraversal.skip((GValue<Long>) literalOrVar);
         } else {
             return graphTraversal.skip((Long) literalOrVar);
@@ -1734,7 +1693,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_tail_Scope_long(final GremlinParser.TraversalMethod_tail_Scope_longContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.parseLong(ctx.integerArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.LONG)) {
+        if (GValue.valueInstanceOf(literalOrVar, Long.class)) {
             return graphTraversal.tail(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
                     (GValue<Long>) literalOrVar);
         } else {
@@ -1749,7 +1708,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public GraphTraversal visitTraversalMethod_tail_long(final GremlinParser.TraversalMethod_tail_longContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.parseLong(ctx.integerArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.LONG)) {
+        if (GValue.valueInstanceOf(literalOrVar, Long.class)) {
             return graphTraversal.tail((GValue<Long>) literalOrVar);
         } else {
             return graphTraversal.tail((Long) literalOrVar);
@@ -1795,9 +1754,8 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_to_Direction_String(final GremlinParser.TraversalMethod_to_Direction_StringContext ctx) {
-        return graphTraversal.to(
-                TraversalEnumParser.parseTraversalEnumFromContext(Direction.class, ctx.traversalDirection()),
-                antlr.genericVisitor.parseStringVarargs(ctx.stringNullableLiteralVarargs()));
+        return graphTraversal.to(TraversalEnumParser.parseTraversalEnumFromContext(Direction.class, ctx.traversalDirection()),
+                antlr.argumentVisitor.parseStringVarargs(ctx.stringNullableArgumentVarargs()));
     }
 
     /**
@@ -1982,7 +1940,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public Traversal visitTraversalMethod_call_string_map(final GremlinParser.TraversalMethod_call_string_mapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapArgument(ctx.genericMapArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return graphTraversal.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (GValue<Map>) literalOrVar);
         else
             return graphTraversal.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (Map) literalOrVar);
@@ -2003,7 +1961,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     @Override
     public Traversal visitTraversalMethod_call_string_map_traversal(final GremlinParser.TraversalMethod_call_string_map_traversalContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapArgument(ctx.genericMapArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return graphTraversal.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (GValue<Map>) literalOrVar, antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
         else
             return graphTraversal.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (Map) literalOrVar, antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
@@ -2190,8 +2148,7 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      */
     @Override
     public GraphTraversal visitTraversalMethod_split_Scope_String(final GremlinParser.TraversalMethod_split_Scope_StringContext ctx) {
-        return graphTraversal.split(
-                TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
+        return graphTraversal.split(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope()),
                 antlr.genericVisitor.parseString(ctx.stringNullableLiteral()));
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java
index f3a1f10e6f..c0e9185baf 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -57,7 +58,7 @@ public class TraversalSourceSelfMethodVisitor extends DefaultGremlinBaseVisitor<
      */
     @Override
     public GraphTraversalSource visitTraversalSourceSelfMethod_withBulk(final GremlinParser.TraversalSourceSelfMethod_withBulkContext ctx) {
-        final boolean useBulk = (boolean) antlr.argumentVisitor.visitBooleanArgument(ctx.booleanArgument());
+        final boolean useBulk = (boolean) antlr.genericVisitor.visitBooleanLiteral(ctx.booleanLiteral());
         return source.withBulk(useBulk);
     }
 
@@ -75,9 +76,9 @@ public class TraversalSourceSelfMethodVisitor extends DefaultGremlinBaseVisitor<
     @Override
     public GraphTraversalSource visitTraversalSourceSelfMethod_withSack(final GremlinParser.TraversalSourceSelfMethod_withSackContext ctx) {
         if (ctx.getChildCount() == 4) {
-            return source.withSack(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
+            return source.withSack(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
         } else {
-            return source.withSack(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()),
+            return source.withSack(antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()),
                     TraversalEnumParser.parseTraversalEnumFromContext(Operator.class, ctx.traversalBiFunction().traversalOperator()));
         }
     }
@@ -90,10 +91,10 @@ public class TraversalSourceSelfMethodVisitor extends DefaultGremlinBaseVisitor<
         if (ctx.getChildCount() < 8) {
             // with 4 children withSideEffect() was called without a reducer specified.
             return source.withSideEffect(antlr.genericVisitor.parseString(ctx.stringLiteral()),
-                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
+                    antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
         } else {
             return source.withSideEffect(antlr.genericVisitor.parseString(ctx.stringLiteral()),
-                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()),
+                    antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()),
                     TraversalEnumParser.parseTraversalEnumFromContext(Operator.class, ctx.traversalBiFunction().traversalOperator()));
         }
     }
@@ -141,7 +142,7 @@ public class TraversalSourceSelfMethodVisitor extends DefaultGremlinBaseVisitor<
             return source.with(antlr.genericVisitor.parseString(ctx.stringLiteral()));
         } else {
             return source.with(antlr.genericVisitor.parseString(ctx.stringLiteral()),
-                    antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
+                    antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()));
         }
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
index 89f4ba910d..7fccb00070 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
@@ -22,8 +22,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
-
 import java.util.Map;
 
 /**
@@ -60,7 +58,7 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
     public GraphTraversal visitTraversalSourceSpawnMethod_addE(final GremlinParser.TraversalSourceSpawnMethod_addEContext ctx) {
         if (ctx.stringArgument() != null) {
             final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(ctx.stringArgument());
-            if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+            if (GValue.valueInstanceOf(literalOrVar, String.class)) {
                 return this.traversalSource.addE((GValue<String>) literalOrVar);
             } else {
                 return this.traversalSource.addE((String) literalOrVar);
@@ -79,7 +77,7 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
     public GraphTraversal visitTraversalSourceSpawnMethod_addV(final GremlinParser.TraversalSourceSpawnMethod_addVContext ctx) {
         if (ctx.stringArgument() != null) {
             final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(ctx.stringArgument());
-            if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) {
+            if (GValue.valueInstanceOf(literalOrVar, String.class)) {
                 return this.traversalSource.addV((GValue<String>) literalOrVar);
             } else {
                 return this.traversalSource.addV((String) literalOrVar);
@@ -112,7 +110,7 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
      */
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_inject(final GremlinParser.TraversalSourceSpawnMethod_injectContext ctx) {
-        return this.traversalSource.inject(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()));
+        return this.traversalSource.inject(antlr.genericVisitor.parseObjectVarargs(ctx.genericLiteralVarargs()));
     }
 
     /**
@@ -132,10 +130,10 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_mergeV_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeV_MapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapNullableArgument(ctx.genericMapNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return this.traversalSource.mergeV((GValue) literalOrVar);
-        else if (GValue.valueInstanceOf(literalOrVar, GType.UNKNOWN) && ((GValue) literalOrVar).get() == null)
-            return this.traversalSource.mergeV((GValue) GValue.ofMap(((GValue) literalOrVar).getName(), null));
+        else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).get() == null)
+            return this.traversalSource.mergeV(GValue.ofMap(((GValue) literalOrVar).getName(), null));
         else
             return this.traversalSource.mergeV((Map) literalOrVar);
     }
@@ -162,9 +160,9 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_mergeE_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeE_MapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapNullableArgument(ctx.genericMapNullableArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
             return this.traversalSource.mergeE((GValue) literalOrVar);
-        else if (GValue.valueInstanceOf(literalOrVar, GType.UNKNOWN) && ((GValue) literalOrVar).get() == null)
+        else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).get() == null)
             return this.traversalSource.mergeE((GValue) GValue.ofMap(((GValue) literalOrVar).getName(), null));
         else
             return this.traversalSource.mergeE((Map) literalOrVar);
@@ -192,8 +190,8 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_call_string_map(final GremlinParser.TraversalSourceSpawnMethod_call_string_mapContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapArgument(ctx.genericMapArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
-            return this.traversalSource.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (GValue<Map>) literalOrVar);
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
+            return this.traversalSource.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (GValue<Map<?,?>>) literalOrVar);
         else
             return this.traversalSource.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (Map) literalOrVar);
     }
@@ -213,8 +211,8 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_call_string_map_traversal(final GremlinParser.TraversalSourceSpawnMethod_call_string_map_traversalContext ctx) {
         final Object literalOrVar = antlr.argumentVisitor.visitGenericMapArgument(ctx.genericMapArgument());
-        if (GValue.valueInstanceOf(literalOrVar, GType.MAP))
-            return this.traversalSource.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (GValue<Map>) literalOrVar, anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
+        if (GValue.valueInstanceOf(literalOrVar, Map.class))
+            return this.traversalSource.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (GValue<Map<?,?>>) literalOrVar, anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
         else
             return this.traversalSource.call(antlr.genericVisitor.parseString(ctx.stringLiteral()), (Map) literalOrVar, anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
index b6e8f7b2b2..5bff3e0e08 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
@@ -789,13 +789,8 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor {
             visit(ctx.stringNullableArgument());
 
             // more arguments to come
-            if (!ctx.stringNullableArgumentVarargs().stringNullableArgument().isEmpty()) {
-                ctx.stringNullableArgumentVarargs().stringNullableArgument().forEach(sna -> {
-                    sb.append(", ");
-                    tryAppendCastToString(sna);
-                    visit(sna);
-                });
-            }
+            if (!ctx.stringNullableArgumentVarargs().isEmpty())  sb.append(", ");
+            visit(ctx.stringNullableArgumentVarargs());
 
             sb.append(")");
             return null;
@@ -803,7 +798,7 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor {
     }
 
     @Override
-    public Void visitStringNullableLiteralVarargs(final GremlinParser.StringNullableLiteralVarargsContext ctx) {
+    public Void visitStringNullableArgumentVarargs(final GremlinParser.StringNullableArgumentVarargsContext ctx) {
         for (int ix = 0; ix < ctx.getChildCount(); ix++) {
             final ParseTree pt = ctx.getChild(ix);
             if (pt instanceof GremlinParser.StringNullableArgumentContext) {
@@ -1000,11 +995,11 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor {
             sb.append("(");
             visit(ctx.traversalCardinality());
             sb.append(", ");
-            tryAppendCastToObject(ctx.genericArgument(0));
-            visit(ctx.genericArgument(0));
+            tryAppendCastToObject(ctx.genericLiteral());
+            visit(ctx.genericLiteral());
             sb.append(", ");
-            tryAppendCastToObject(ctx.genericArgument(1));
-            visit(ctx.genericArgument(1));
+            tryAppendCastToObject(ctx.genericArgument());
+            visit(ctx.genericArgument());
             sb.append(")");
             return null;
         } else {
@@ -1017,8 +1012,8 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor {
         final String step = ctx.getChild(0).getText();
         sb.append(convertToPascalCase(step));
         sb.append("(");
-        tryAppendCastToString(ctx.stringArgument());
-        visit(ctx.stringArgument());
+        tryAppendCastToString(ctx.stringLiteral());
+        visit(ctx.stringLiteral());
         sb.append(")");
         return null;
     }
@@ -1314,6 +1309,11 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor {
             sb.append("(object) ");
     }
 
+    private void tryAppendCastToObject(final GremlinParser.GenericLiteralContext ctx) {
+        if (ctx.nullLiteral() != null)
+            sb.append("(object) ");
+    }
+
     private boolean isCalledAsFirstStepInAnonymousTraversal(final ParseTree stepToTest) {
         final ParseTree parent = stepToTest.getParent();
         final ParseTree parentParent = parent.getParent();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
index e2bb9df27e..31839cefad 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
@@ -201,7 +201,7 @@ public class GroovyTranslateVisitor extends TranslateVisitor {
     public Void visitTraversalMethod_hasLabel_String_String(final GremlinParser.TraversalMethod_hasLabel_String_StringContext ctx) {
         // special handling for resolving ambiguous invocations of the hasLabel() step. coerces to the string form
         if (ctx.getChildCount() > 4 && ctx.getChild(2).getText().equals("null")) {
-            final GremlinParser.StringNullableArgumentVarargsContext varArgs = ctx.stringNullableArgumentVarargs();
+            final GremlinParser.StringNullableArgumentVarargsContext varArgs = (GremlinParser.StringNullableArgumentVarargsContext) ctx.getChild(4);
             sb.append(ctx.getChild(0).getText());
             sb.append("((String) null, ");
 
@@ -229,25 +229,28 @@ public class GroovyTranslateVisitor extends TranslateVisitor {
     * inject() ends up being ambiguous with groovy's jdk extension of inject(Object initialValue, Closure closure)
     */
     private Void handleInject(final ParserRuleContext ctx) {
-        if (ctx.getChildCount() > 3 && ctx.getChild(2) instanceof GremlinParser.GenericArgumentVarargsContext) {
-            final GremlinParser.GenericArgumentVarargsContext varArgs = (GremlinParser.GenericArgumentVarargsContext) ctx.getChild(2);
-            if (varArgs.getChildCount() > 2 && "null".equals(varArgs.getChild(2).getText())) {
-                sb.append(ctx.getChild(0).getText());
-                sb.append("(");
-                for (int i = 0; i < varArgs.getChildCount(); i += 2) {
-                    if (i == 2) {
-                        sb.append("(Object) null");
-                    } else {
-                        visit(varArgs.getChild(i));
+        if (ctx.getChildCount() > 3 && ctx.getChild(2) instanceof GremlinParser.GenericLiteralVarargsContext) {
+            final GremlinParser.GenericLiteralVarargsContext varArgs = (GremlinParser.GenericLiteralVarargsContext) ctx.getChild(2);
+            if (varArgs.getChildCount() == 1 && varArgs.getChild(0) instanceof GremlinParser.GenericLiteralExprContext) {
+                GremlinParser.GenericLiteralExprContext injectArgs = (GremlinParser.GenericLiteralExprContext) varArgs.getChild(0);
+                if (injectArgs.getChildCount() > 2 && "null".equals(injectArgs.getChild(2).getText())) {
+                    sb.append(ctx.getChild(0).getText());
+                    sb.append("(");
+                    for (int i = 0; i < injectArgs.getChildCount(); i += 2) {
+                        if (i == 2) {
+                            sb.append("(Object) null");
+                        } else {
+                            visit(injectArgs.getChild(i));
+                        }
+
+                        if (i < injectArgs.getChildCount() - 1) {
+                            sb.append(", ");
+                        }
                     }
 
-                    if (i < varArgs.getChildCount() - 1) {
-                        sb.append(", ");
-                    }
+                    sb.append(")");
+                    return null;
                 }
-
-                sb.append(")");
-                return null;
             }
         }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
index b7b6ed08d5..4b191cb3d4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
@@ -244,6 +244,11 @@ public class PythonTranslateVisitor extends AbstractTranslateVisitor {
 
     @Override
     public Void visitGenericSetLiteral(final GremlinParser.GenericSetLiteralContext ctx) {
+        if (ctx.genericLiteral().isEmpty()) {
+            // handle the empty set case separately as `{}` in python is an empty dict, not a set
+            sb.append("set()");
+            return null;
+        }
         sb.append("{");
         for (int i = 0; i < ctx.genericLiteral().size(); i++) {
             final GremlinParser.GenericLiteralContext genericLiteralContext = ctx.genericLiteral(i);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java
index ed49363dc3..b41ada7f7c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java
@@ -80,7 +80,7 @@ public final class VertexProgramStrategy extends AbstractTraversalStrategy<Trave
         Step<?, ?> currentStep = traversal.getEndStep();
         final Set<String> currentLabels = new HashSet<>();
         while (!(currentStep instanceof EmptyStep)) {
-            if (currentStep instanceof VertexComputing && !(currentStep instanceof ProgramVertexProgramStep)) {  // todo: is there a general solution?
+            if (currentStep instanceof VertexComputing && !(currentStep instanceof ProgramVertexProgramStep)) {
                 currentLabels.addAll(currentStep.getLabels());
                 currentStep.clearLabels();
             } else {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/traversal/AbstractRemoteTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/traversal/AbstractRemoteTraversal.java
index 869b947f3d..616a835f09 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/traversal/AbstractRemoteTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/traversal/AbstractRemoteTraversal.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.remote.traversal;
 
 import org.apache.tinkerpop.gremlin.process.remote.traversal.step.map.RemoteStep;
 import org.apache.tinkerpop.gremlin.process.traversal.GremlinLang;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
@@ -62,6 +63,16 @@ public abstract class AbstractRemoteTraversal<S,E> implements RemoteTraversal<S,
         throw new UnsupportedOperationException("Remote traversals do not support this method");
     }
 
+    @Override
+    public GValueManager getGValueManager() {
+        throw new UnsupportedOperationException("Remote traversals do not support this method");
+    }
+
+    @Override
+    public void setGValueManager(final GValueManager gValueManager) {
+        throw new UnsupportedOperationException("Remote traversals do not support this method");
+    }
+
     @Override
     public List<Step> getSteps() {
         throw new UnsupportedOperationException("Remote traversals do not support this method");
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
new file mode 100644
index 0000000000..6ef7c1db71
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal;
+
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The {@code GValueManager} class is responsible for managing the state of {@link GValue} instances and their
+ * associations with `Step` objects in a traversal. This class ensures that `GValue` instances are properly extracted
+ * and stored in a registry, allowing for dynamic query optimizations and state management during traversal execution.
+ */
+public final class GValueManager implements Serializable, Cloneable {
+
+    private final Map<String, GValue<?>> gValueRegistry = new HashMap<>();
+    /**
+     * Tracks pinned values for faster lookup.
+     */
+    private final Set<String> pinnedVariables = new HashSet<>();
+
+    /**
+     * Creates a new empty GValueManager.
+     */
+    public GValueManager() {
+        this(Collections.emptyMap(), Collections.emptySet());
+    }
+
+    private GValueManager(final Map<String, GValue<?>> gValueRegistry, final Set<String> pinnedVariables) {
+        this.gValueRegistry.putAll(gValueRegistry);
+        this.pinnedVariables.addAll(pinnedVariables);
+    }
+
+    /**
+     * Merges the internal registries of the current {@code GValueManager} into another {@code GValueManager}.
+     * Transfers the state of the {@code gValueRegistry} and {@code stepRegistry} from the current instance to the
+     * specified target instance.
+     *
+     * @param other the target {@code GValueManager} into which the current instance's registries will be merged
+     */
+    public void mergeInto(final GValueManager other) {
+        other.register(gValueRegistry.values());
+        other.pinGValues(pinnedVariables);
+    }
+
+    /**
+     * Gets the set of variable names used in this traversal.
+     */
+    public Set<String> getVariableNames() {
+        return Collections.unmodifiableSet(gValueRegistry.keySet());
+    }
+
+    /**
+     * Gets the set of variable names used in this traversal which have not been pinned to specific values.
+     */
+    public Set<String> getUnpinnedVariableNames() {
+        final Set<String> variableNames = new HashSet<>(gValueRegistry.keySet());
+        variableNames.removeAll(pinnedVariables);
+        return Collections.unmodifiableSet(variableNames);
+    }
+
+    /**
+     * Gets the set of variable names used in this traversal which have been pinned to specific values.
+     */
+    public Set<String> getPinnedVariableNames() {
+        return Collections.unmodifiableSet(pinnedVariables);
+    }
+
+    /**
+     * Gets the set of {@link GValue} objects used in this traversal.
+     */
+    public Set<GValue<?>> getGValues() {
+        return Set.copyOf(gValueRegistry.values());
+    }
+
+    /**
+     * Gets the set of pinned GValues used in this traversal.
+     */
+    public Set<GValue<?>> getPinnedGValues() {
+        return pinnedVariables.stream().map(gValueRegistry::get).collect(Collectors.toUnmodifiableSet());
+    }
+
+    /**
+     * Determines whether the manager has step registrations.
+     */
+    public boolean hasVariables() {
+        return !gValueRegistry.isEmpty();
+    }
+
+    /**
+     * Determines whether the manager has unpinned GValues.
+     */
+    public boolean hasUnpinnedVariables() {
+        return !getUnpinnedVariableNames().isEmpty();
+    }
+
+    /**
+     * Pins any GValue with the provided name. This should be called anytime an optimization
+     * alters the Traversal based on the current value of a GValue. This indicates that the resulting traversal is only
+     * valid for the current value of this GValue, and is not generalizable to any parameter value.
+     *
+     * @param name the name of the GValue to be pinned
+     * @return true if name was previously unpinned, false otherwise.
+     * @throws IllegalArgumentException if no GValue of the given name is registered
+     */
+    public boolean pinVariable(final String name) {
+        if (name == null) {
+            return false;
+        }
+        if (!gValueRegistry.containsKey(name)) {
+            throw new IllegalArgumentException(String.format("Unable to pin variable '%s' because it is not registered", name));
+        }
+        return pinnedVariables.add(name);
+    }
+
+    /**
+     * Creates a deep copy of this GValueManager with cloned GValue instances.
+     *
+     * @return a new GValueManager instance with cloned state
+     * @throws CloneNotSupportedException if cloning fails
+     */
+    @Override
+    public GValueManager clone() throws CloneNotSupportedException {
+        final Map<String, GValue<?>> clonedRegistry = new HashMap<>();
+        for (final Map.Entry<String, GValue<?>> entry : gValueRegistry.entrySet()) {
+            // clone each gValue
+            clonedRegistry.put(entry.getKey(), entry.getValue().clone());
+        }
+        return new GValueManager(clonedRegistry, new HashSet<>(pinnedVariables));
+    }
+
+    /**
+     * Pin a collection of {@link GValue} by name from the internal registry. Iteratively processes each
+     * {@link GValue} name to pin it to its current value.
+     */
+    public void pinGValues(final Set<String> names) {
+        names.stream().filter(Objects::nonNull).forEach(this::pinVariable);
+    }
+
+    /**
+     * Pin a collection of {@link GValue} instances from the internal registry. Iteratively processes each
+     * {@link GValue} to pin it to its current value.
+     */
+    public void pinGValues(final Collection<GValue<?>> gValues) {
+        pinGValues(gValues.stream().filter(v -> v != null && v.isVariable()).map(GValue::getName).collect(Collectors.toSet()));
+    }
+
+    /**
+     * Registers a GValue in the internal registry if it represents a variable. Non-variables are not registered.
+     *
+     * @param gValue the GValue to register
+     * @throws IllegalArgumentException if a different GValue with the same name is already registered
+     */
+    public void register(final GValue<?> gValue) {
+        if (gValue.isVariable()) {
+            if (gValueRegistry.containsKey(gValue.getName()) && !gValueRegistry.get(gValue.getName()).equals(gValue)) {
+                throw new IllegalArgumentException(String.format("Unable to register both %s and %s in a single traversal", gValueRegistry.get(gValue.getName()), gValue));
+            }
+            gValueRegistry.put(gValue.getName(), gValue);
+        }
+    }
+
+    /**
+     * Registers a collection of GValues in the internal registry. Non-variables are not registered.
+     *
+     * @param gValues the collection of GValues to register
+     */
+    public void register(final Collection<GValue<?>> gValues) {
+        gValues.forEach(this::register);
+    }
+
+    /**
+     * Updates the value of a registered variable.
+     *
+     * @param name the name of the variable to update
+     * @param value the new value for the variable
+     * @throws IllegalArgumentException if the variable is not registered or is already pinned
+     */
+    public void updateVariable(final String name, final Object value) {
+        if (!gValueRegistry.containsKey(name)) {
+            throw new IllegalArgumentException(String.format("Unable to update variable '%s' as it has not been registered in this traversal", name));
+        } else if (pinnedVariables.contains(name)) {
+            throw new IllegalArgumentException(String.format("Unable to update variable '%s' as it is already pinned", name));
+        }
+        gValueRegistry.put(name, GValue.of(name, value));
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 0b1b51023f..2cd9150b4f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -18,14 +18,20 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.List;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -38,18 +44,41 @@ import java.util.stream.Collectors;
 public class P<V> implements Predicate<V>, Serializable, Cloneable {
 
     protected PBiPredicate<V, V> biPredicate;
-    protected V value;
+    protected Map<String, V> variables = new HashMap<>();
+    protected Collection<V> literals = Collections.EMPTY_LIST;
+    private boolean isCollection = false;
 
     public P(final PBiPredicate<V, V> biPredicate, final V value) {
-        this.value = value;
+        setValue(value);
         this.biPredicate = biPredicate;
     }
 
+    public P(final PBiPredicate<V, V> biPredicate, final GValue<V> value) {
+        if (value != null) {
+            if (value.isVariable()) {
+                variables.put(value.getName(), value.get());
+            } else {
+                literals = Collections.singleton(value.get());
+            }
+        } else {
+            this.literals = Collections.singleton(null);
+        }
+        this.isCollection = false;
+        this.biPredicate = biPredicate;
+    }
+
+    protected P(final PBiPredicate<V, V> biPredicate, final Collection<V> literals, final Map<String, V> variables, final boolean isCollection) {
+        this.biPredicate = biPredicate;
+        this.variables.putAll(variables);
+        this.literals = new ArrayList<>(literals);
+        this.isCollection = isCollection;
+    }
+
     public PBiPredicate<V, V> getBiPredicate() {
         return this.biPredicate;
     }
 
-    /*
+    /**
      * Get the name of the predicate
      */
     public String getPredicateName() { return biPredicate.getPredicateName(); }
@@ -58,28 +87,65 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
      * Gets the current value to be passed to the predicate for testing.
      */
     public V getValue() {
-        return this.value;
+        if (isCollection) {
+            Collection<V> values = this.literals.stream().collect(Collectors.toList());
+            values.addAll(this.variables.values());
+            return (V) values;
+        } else if (!this.literals.isEmpty()) {
+            return this.literals.iterator().next();
+        } else if (!this.variables.isEmpty()) {
+            return this.variables.values().iterator().next();
+        }
+        return null;
     }
 
     public void setValue(final V value) {
-        this.value = value;
+        variables.clear();
+        literals = Collections.EMPTY_LIST;
+
+        if (value == null) {
+            isCollection = false;
+            this.literals = Collections.singleton(null);
+        } else if (value instanceof GValue) {
+            variables.put(((GValue<V>) value).getName(), ((GValue<V>) value).get());
+            isCollection = false;
+        } else if (value instanceof Collection) {
+            isCollection = true;
+            if (((Collection<?>) value).stream().anyMatch(v -> v instanceof GValue)) {
+                this.literals = new ArrayList<>();
+                for (Object v : ((Collection<?>) value)) {
+                    // Separate variables and literals
+                    if (v instanceof GValue) {
+                        if (((GValue<V>) v).isVariable()) {
+                            variables.put(((GValue<V>) v).getName(), ((GValue<V>) v).get());
+                        } else {
+                            literals.add(((GValue<V>) v).get());
+                        }
+                    } else {
+                        literals.add((V) v);
+                    }
+                }
+            } else {
+                literals = (Collection<V>) value; // Retain original collection when possible
+            }
+        } else {
+            isCollection = false;
+            this.literals = Collections.singleton(value);
+        }
     }
 
     @Override
     public boolean test(final V testValue) {
-        // this might be a bunch of GValue that need to be resolved. zomg
-        if (this.value instanceof List) {
-            return this.biPredicate.test(testValue, (V) ((List) this.value).stream().map(GValue::valueOf).collect(Collectors.toList()));
-        } else {
-            return this.biPredicate.test(testValue, (V) GValue.valueOf(this.value));
-        }
+        return this.biPredicate.test(testValue, this.getValue());
     }
 
     @Override
     public int hashCode() {
         int result = this.biPredicate.hashCode();
-        if (null != this.value)
-            result ^= this.value.hashCode();
+        if (null != this.variables)
+            result ^= this.variables.hashCode();
+        if (null != this.literals)
+            result ^= this.literals.hashCode();
         return result;
     }
 
@@ -88,17 +154,18 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return other instanceof P &&
                 ((P) other).getClass().equals(this.getClass()) &&
                 ((P) other).getBiPredicate().equals(this.biPredicate) &&
-                ((((P) other).getValue() == null && this.value == null) || ((P) other).getValue().equals(this.value));
+                ((((P) other).variables == null && this.variables == null) || (((P) other).variables != null && ((P) other).variables.equals(this.variables))) &&
+                ((((P) other).literals == null && this.literals == null) || (((P) other).literals != null && CollectionUtils.isEqualCollection(((P) other).literals, this.literals)));
     }
 
     @Override
     public String toString() {
-        return null == this.value ? this.biPredicate.toString() : this.biPredicate.toString() + "(" + this.value + ")";
+        return null == this.getValue() ? this.biPredicate.toString() : this.biPredicate.toString() + "(" + this.getValue() + ")";
     }
 
     @Override
     public P<V> negate() {
-        return new P<>(this.biPredicate.negate(), this.value);
+        return new P<>(this.biPredicate.negate(), this.literals, this.variables, this.isCollection);
     }
 
     @Override
@@ -123,6 +190,24 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         }
     }
 
+    public boolean isParameterized() {
+        return !variables.isEmpty();
+    }
+
+    public void updateVariable(final String name, final Object value) {
+        if (variables.containsKey(name)) {
+            variables.put(name, (V) value);
+        }
+    }
+
+    public Set<GValue<?>> getGValues() {
+        Set<GValue<?>> results = new HashSet<>();
+        for (Map.Entry<String, V> entry : variables.entrySet()) {
+            results.add(GValue.of(entry.getKey(), entry.getValue()));
+        }
+        return results;
+    }
+
     //////////////// statics
 
     /**
@@ -133,6 +218,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
     public static <V> P<V> eq(final V value) {
         return new P(Compare.eq, value);
     }
+// TODO:: Subgraph.feature tests lead to cast exception when this overload is present.
+//    /**
+//     * Determines if values are equal.
+//     *
+//     * @since 3.8.0
+//     */
+//    public static <V> P<V> eq(final GValue<V> value) {
+//        return new P(Compare.eq, value);
+//    }
 
     /**
      * Determines if values are not equal.
@@ -143,6 +237,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new P(Compare.neq, value);
     }
 
+    /**
+     * Determines if values are not equal.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> neq(final GValue<V> value) {
+        return new P(Compare.neq, value);
+    }
+
     /**
      * Determines if a value is less than another.
      *
@@ -152,6 +255,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new P(Compare.lt, value);
     }
 
+    /**
+     * Determines if a value is less than another.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> lt(final GValue<V> value) {
+        return new P(Compare.lt, value);
+    }
+
     /**
      * Determines if a value is less than or equal to another.
      *
@@ -161,6 +273,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new P(Compare.lte, value);
     }
 
+    /**
+     * Determines if a value is less than or equal to another.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> lte(final GValue<V> value) {
+        return new P(Compare.lte, value);
+    }
+
     /**
      * Determines if a value is greater than another.
      *
@@ -170,6 +291,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new P(Compare.gt, value);
     }
 
+    /**
+     * Determines if a value is greater than another.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> gt(final GValue<V> value) {
+        return new P(Compare.gt, value);
+    }
+
     /**
      * Determines if a value is greater than or equal to another.
      *
@@ -179,6 +309,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new P(Compare.gte, value);
     }
 
+    /**
+     * Determines if a value is greater than or equal to another.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> gte(final GValue<V> value) {
+        return new P(Compare.gte, value);
+    }
+
     /**
      * Determines if a value is within (exclusive) the range of the two specified values.
      *
@@ -188,6 +327,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new AndP<V>(Arrays.asList(new P(Compare.gt, first), new P(Compare.lt, second)));
     }
 
+    /**
+     * Determines if a value is within (exclusive) the range of the two specified values.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> inside(final GValue<V> first, final GValue<V> second) {
+        return new AndP<V>(Arrays.asList(new P(Compare.gt, first), new P(Compare.lt, second)));
+    }
+
     /**
      * Determines if a value is not within (exclusive) of the range of the two specified values.
      *
@@ -197,6 +345,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new OrP<V>(Arrays.asList(new P(Compare.lt, first), new P(Compare.gt, second)));
     }
 
+    /**
+     * Determines if a value is not within (exclusive) of the range of the two specified values.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> outside(final GValue<V> first, final GValue<V> second) {
+        return new OrP<V>(Arrays.asList(new P(Compare.lt, first), new P(Compare.gt, second)));
+    }
+
     /**
      * Determines if a value is within (inclusive) of the range of the two specified values.
      *
@@ -206,6 +363,15 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return new AndP<V>(Arrays.asList(new P(Compare.gte, first), new P(Compare.lt, second)));
     }
 
+    /**
+     * Determines if a value is within (inclusive) of the range of the two specified values.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> between(final GValue<V> first, final GValue<V> second) {
+        return new AndP<V>(Arrays.asList(new P(Compare.gte, first), new P(Compare.lt, second)));
+    }
+
     /**
      * Determines if a value is within the specified list of values. If the array of arguments itself is {@code null}
      * then the argument is treated as {@code Object[1]} where that single value is {@code null}.
@@ -213,7 +379,18 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
      * @since 3.0.0-incubating
      */
     public static <V> P<V> within(final V... values) {
-        final V[] v = null == values ? (V[]) new Object[] { null } : values;
+        final V[] v = null == values ? (V[]) new Object[] { null } : (V[]) values;
+        return P.within(Arrays.asList(v));
+    }
+
+    /**
+     * Determines if a value is within the specified list of values. If the array of arguments itself is {@code null}
+     * then the argument is treated as {@code Object[1]} where that single value is {@code null}.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> within(final GValue<V>... values) {
+        final V[] v = null == values ? (V[]) new Object[] { null } : (V[]) values;
         return P.within(Arrays.asList(v));
     }
 
@@ -239,6 +416,17 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
         return P.without(Arrays.asList(v));
     }
 
+    /**
+     * Determines if a value is not within the specified list of values. If the array of arguments itself is {@code null}
+     * then the argument is treated as {@code Object[1]} where that single value is {@code null}.
+     *
+     * @since 3.8.0
+     */
+    public static <V> P<V> without(final GValue<V>... values) {
+        final V[] v = null == values ? (V[]) new Object[] { null } : (V[]) values;
+        return P.without(Arrays.asList(v));
+    }
+
     /**
      * Determines if a value is not within the specified list of values. Calling this with {@code null} is the same as
      * calling {@link #within(Object[])} using {@code null}.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/SackFunctions.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/SackFunctions.java
index 3985efe699..4752bf0b32 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/SackFunctions.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/SackFunctions.java
@@ -19,7 +19,6 @@
 
 package org.apache.tinkerpop.gremlin.process.traversal;
 
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
 
 import java.util.function.Consumer;
@@ -43,10 +42,10 @@ public final class SackFunctions {
             public void accept(final TraverserSet<Object> traverserSet) {
                 Number total = 0.0;
                 for (final Traverser.Admin<Object> traverser : traverserSet) {
-                    total = add(total, mul(GValue.numberOf(traverser.sack()), traverser.bulk()));
+                    total = add(total, mul(traverser.sack(), traverser.bulk()));
                 }
                 for (final Traverser.Admin<Object> traverser : traverserSet) {
-                    traverser.sack(div(mul(GValue.numberOf(traverser.sack()), traverser.bulk()), total));
+                    traverser.sack(div(mul(traverser.sack(), traverser.bulk()), total));
                 }
             }
         }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
index c88b4ef58e..f7060d82b6 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
@@ -18,6 +18,11 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal;
 
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+
+import java.util.Collection;
+import java.util.Map;
+
 /**
  * Predefined {@code Predicate} values that can be used as {@code String} filters.
  *
@@ -29,19 +34,22 @@ public class TextP extends P<String> {
         super(biPredicate, value);
     }
 
-    @Override
-    public boolean equals(final Object other) {
-        return other instanceof TextP && super.equals(other);
+    public TextP(final PBiPredicate<String, String> biPredicate, final GValue<String> value) {
+        super(biPredicate, value);
+    }
+
+    protected TextP(final PBiPredicate<String, String> biPredicate, final Collection<String> literals, final Map<String, String> variables) {
+        super(biPredicate, literals, variables, false);
     }
 
     @Override
-    public String toString() {
-        return null == this.value ? this.biPredicate.toString() : this.biPredicate.toString() + "(" + this.value + ")";
+    public boolean equals(final Object other) {
+        return other instanceof TextP && super.equals(other);
     }
 
     @Override
     public TextP negate() {
-        return new TextP(this.biPredicate.negate(), this.value);
+        return new TextP(this.biPredicate.negate(), this.literals, this.variables);
     }
 
     public TextP clone() {
@@ -59,6 +67,15 @@ public class TextP extends P<String> {
         return new TextP(Text.startingWith, value);
     }
 
+    /**
+     * Determines if String does start with the given value.
+     *
+     * @since 3.8.0
+     */
+    public static TextP startingWith(final GValue<String> value) {
+        return new TextP(Text.startingWith, value);
+    }
+
     /**
      * Determines if String does not start with the given value.
      *
@@ -68,6 +85,15 @@ public class TextP extends P<String> {
         return new TextP(Text.notStartingWith, value);
     }
 
+    /**
+     * Determines if String does not start with the given value.
+     *
+     * @since 3.8.0
+     */
+    public static TextP notStartingWith(final GValue<String> value) {
+        return new TextP(Text.notStartingWith, value);
+    }
+
     /**
      * Determines if String does start with the given value.
      *
@@ -77,6 +103,15 @@ public class TextP extends P<String> {
         return new TextP(Text.endingWith, value);
     }
 
+    /**
+     * Determines if String does start with the given value.
+     *
+     * @since 3.8.0
+     */
+    public static TextP endingWith(final GValue<String> value) {
+        return new TextP(Text.endingWith, value);
+    }
+
     /**
      * Determines if String does not start with the given value.
      *
@@ -86,6 +121,15 @@ public class TextP extends P<String> {
         return new TextP(Text.notEndingWith, value);
     }
 
+    /**
+     * Determines if String does not start with the given value.
+     *
+     * @since 3.8.0
+     */
+    public static TextP notEndingWith(final GValue<String> value) {
+        return new TextP(Text.notEndingWith, value);
+    }
+
     /**
      * Determines if String does contain the given value.
      *
@@ -95,6 +139,15 @@ public class TextP extends P<String> {
         return new TextP(Text.containing, value);
     }
 
+    /**
+     * Determines if String does contain the given value.
+     *
+     * @since 3.8.0
+     */
+    public static TextP containing(final GValue<String> value) {
+        return new TextP(Text.containing, value);
+    }
+
     /**
      * Determines if String does not contain the given value.
      *
@@ -103,6 +156,15 @@ public class TextP extends P<String> {
     public static TextP notContaining(final String value) {
         return new TextP(Text.notContaining, value);
     }
+
+    /**
+     * Determines if String does not contain the given value.
+     *
+     * @since 3.8.0
+     */
+    public static TextP notContaining(final GValue<String> value) {
+        return new TextP(Text.notContaining, value);
+    }
     
     /**           
      * Determines if String has a match with the given regex pattern. The TinkerPop reference implementation uses
@@ -116,6 +178,18 @@ public class TextP extends P<String> {
         return new TextP(new Text.RegexPredicate(value, false), value);
     }
 
+    /**
+     * Determines if String has a match with the given regex pattern. The TinkerPop reference implementation uses
+     * Java syntax for regex. The string is considered a match to the pattern if any substring matches the pattern. It
+     * is therefore important to use the appropriate boundary matchers (e.g. `$` for end of a line) to ensure a proper
+     * match.
+     *
+     * @since 3.8.0
+     */
+    public static TextP regex(final GValue<String> value) {
+        return new TextP(new Text.RegexPredicate(value.get(), false), value);
+    }
+
     /**           
      * Determines if String has no match with the given regex pattern and the reference implementation treats it as a
      * simple negation of the evaluation of the pattern match of {@link #regex(String)}.
@@ -125,4 +199,14 @@ public class TextP extends P<String> {
     public static TextP notRegex(final String value) {
         return new TextP(new Text.RegexPredicate(value, true), value);
     }
+
+    /**
+     * Determines if String has no match with the given regex pattern and the reference implementation treats it as a
+     * simple negation of the evaluation of the pattern match of {@link #regex(String)}.
+     *
+     * @since 3.8.0
+     */
+    public static TextP notRegex(final GValue<String> value) {
+        return new TextP(new Text.RegexPredicate(value.get(), true), value);
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
index 005eb8c3d0..f095d3add1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
@@ -329,6 +329,16 @@ public interface Traversal<S, E> extends Iterator<E>, Serializable, Cloneable, A
          */
         public GremlinLang getGremlinLang();
 
+        /**
+         * Get the {@link GValueManager} associated with this traversal.
+         */
+        public GValueManager getGValueManager();
+
+        /**
+         * Set the {@link GValueManager} associated with this traversal.
+         */
+        public void setGValueManager(final GValueManager gValueManager);
+
         /**
          * Add an iterator of {@link Traverser.Admin} objects to the head/start of the traversal. Users should
          * typically not need to call this method. For dynamic inject of data, they should use {@link InjectStep}.
@@ -384,7 +394,8 @@ public interface Traversal<S, E> extends Iterator<E>, Serializable, Cloneable, A
         public <S2, E2> Traversal.Admin<S2, E2> addStep(final int index, final Step<?, ?> step) throws IllegalStateException;
 
         /**
-         * Remove a {@link Step} from the traversal.
+         * Remove a {@link Step} from the traversal. Those overriding this method should take care to reset state for
+         * the step in the {@link GValueManager}. By default, this requirement is delegated to {@link #removeStep(int)}.
          *
          * @param step the step to remove
          * @param <S2> the new start type of the traversal (if the removed step was a start step)
@@ -397,7 +408,8 @@ public interface Traversal<S, E> extends Iterator<E>, Serializable, Cloneable, A
         }
 
         /**
-         * Remove a {@link Step} from the traversal.
+         * Remove a {@link Step} from the traversal. Those implementing this method should take care to reset state for
+         * the step in the {@link GValueManager}.
          *
          * @param index the location in the traversal of the step to be evicted
          * @param <S2>  the new start type of the traversal (if the removed step was a start step)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
index c36afa8081..f572604fbb 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
@@ -34,6 +34,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.Partit
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SackStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.GValueReductionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ReferenceElementStrategy;
@@ -254,6 +255,7 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
             // finalization
             put(MatchAlgorithmStrategy.class.getSimpleName(), MatchAlgorithmStrategy.class);
             put(ReferenceElementStrategy.class.getSimpleName(), ReferenceElementStrategy.class);
+            put(GValueReductionStrategy.class.getSimpleName(), GValueReductionStrategy.class);
 
             // optimizations
             put(ProductiveByStrategy.class.getSimpleName(), ProductiveByStrategy.class);
@@ -285,7 +287,8 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
                     PathRetractionStrategy.instance(),
                     LazyBarrierStrategy.instance(),
                     ProfileStrategy.instance(),
-                    StandardVerificationStrategy.instance());
+                    StandardVerificationStrategy.instance(),
+                    GValueReductionStrategy.instance());
             registerStrategies(Graph.class, graphStrategies);
             registerStrategies(EmptyGraph.class, new DefaultTraversalStrategies());
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategy.java
index 2ed42e10e8..530bf411fb 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategy.java
@@ -20,9 +20,11 @@ package org.apache.tinkerpop.gremlin.process.traversal;
 
 import org.apache.commons.configuration2.BaseConfiguration;
 import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
 import org.apache.tinkerpop.gremlin.util.GremlinDisabledListDelimiterHandler;
 
@@ -31,21 +33,38 @@ import java.util.Collections;
 import java.util.Set;
 
 /**
- * A {@link TraversalStrategy} defines a particular atomic operation for mutating a {@link Traversal} prior to its evaluation.
- * There are 5 pre-defined "traversal categories": {@link DecorationStrategy}, {@link OptimizationStrategy}, {@link ProviderOptimizationStrategy}, {@link FinalizationStrategy}, and {@link VerificationStrategy}.
- * Strategies within a category are sorted amongst themselves and then category sorts are applied in the ordered specified previous.
- * That is, decorations are applied, then optimizations, then provider optimizations, then finalizations, and finally, verifications.
- * If a strategy does not fit within the specified categories, then it can simply implement {@link TraversalStrategy} and can have priors/posts that span categories.
+ * A {@link TraversalStrategy} defines a particular atomic operation for mutating a {@link Traversal} prior to its
+ * evaluation. There are 5 pre-defined "traversal categories": {@link DecorationStrategy}, {@link OptimizationStrategy},
+ * {@link ProviderOptimizationStrategy}, {@link FinalizationStrategy}, and {@link VerificationStrategy}.
+ * Strategies within a category are sorted amongst themselves, and then category sorts are applied in the ordered
+ * specified previously. That is, decorations are applied, then optimizations, then provider optimizations, then
+ * finalizations, and finally, verifications. If a strategy does not fit within the specified categories, then it can
+ * simply implement {@link TraversalStrategy} and can have priors/posts that span categories.
  * <p/>
- * A traversal strategy should be a final class as various internal operations on a strategy are based on its ability to be assigned to more general classes.
- * A traversal strategy should typically be stateless with a public static <code>instance()</code> method.
- * However, at limit, a traversal strategy can have a state defining constructor (typically via a "builder"), but that state can not mutate once instantiated.
+ * A traversal strategy should be a final class as various internal operations on a strategy are based on its ability to
+ * be assigned to more general classes. A traversal strategy should typically be stateless with a public static
+ * <code>instance()</code> method. However, at limit, a traversal strategy can have a state-defining constructor
+ * (typically via a "builder"), but that state cannot mutate once instantiated.
+ * <p/>
+ * Given that a traversal strategy can completely rewrite a traversal, it must take into account the
+ * {@link GValueManager} state while doing so. The {@link GValueManager} maintains a record of "pinned" and "free"
+ * variables. When a new traversal is created, all variables are considered free, as any value may be substituted for
+ * a variable without restriction. Strategies may rewrite a traversal in a manner which is valid for the current value
+ * of a {@link GValue}, but would lead to incorrect behaviour if that variable were later updated arbitrarily. An
+ * example of this is {@link EarlyLimitStrategy}, which will replace a traversal such as
+ * `limit(GValue.of("x", 5)).valueMap().range(5, 10)` with simply `discard()`, as the limit and range prevent the
+ * traversal from being productive. This optimization is not generalizable to any values of `x` however, as any value of
+ * `x` greater than 5 is expected to produce results. In cases such as these, the strategy must ensure that the variable
+ * `x` is "pinned" in the {@link GValueManager}, to indicate it is no longer free to be updated arbitrarily.
  *
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Matthias Broecheler (me@matthiasb.com)
  */
 public interface TraversalStrategy<S extends TraversalStrategy> extends Serializable, Comparable<Class<? extends TraversalStrategy>> {
 
+    /**
+     * The transformation the strategy applies to the traversal.
+     */
     public void apply(final Traversal.Admin<?, ?> traversal);
 
     /**
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 9cbdc569e0..a8620c2ae2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -42,15 +42,33 @@ import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTra
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ColumnTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.FunctionTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.LoopTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.PredicateTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.TokenTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.TrueTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.IsStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeStepPlaceholder.RangeGlobalStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeStepPlaceholder.RangeLocalStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TailGlobalStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TailLocalStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeStepContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PropertyAdding;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStepContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStepContract;
 import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ReadWriting;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TimesModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
@@ -81,7 +99,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TimeLimitStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TraversalFilterStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WherePredicateStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTraversalStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
@@ -130,7 +147,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxLocalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanLocalStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinLocalStep;
@@ -167,7 +183,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.ToUpperGlobalStep
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ToUpperLocalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalFlatMapStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalMapStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalSelectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.TreeStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.TrimGlobalStep;
@@ -191,6 +206,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StartStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TraversalSideEffectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeSideEffectStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStepContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStepContract;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
@@ -390,14 +407,16 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     public default GraphTraversal<S, Vertex> V(final Object... vertexIdsOrElements) {
         // a single null is [null]
         final Object[] ids = null == vertexIdsOrElements ? new Object[] { null } : vertexIdsOrElements;
-        // convert any vertices to ids
-        for (int i = 0; i < ids.length; i++) {
-            if (ids[i] instanceof Vertex) {
-                ids[i] = ((Vertex) ids[i]).id();
-            }
-        }
         this.asAdmin().getGremlinLang().addStep(Symbols.V, ids);
-        return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Vertex.class, false, ids));
+
+        GraphStepContract<E, Vertex> step;
+        if (GValue.containsGValues(ids)) {
+            step = new GraphStepPlaceholder<>(this.asAdmin(), Vertex.class, false, GValue.ensureGValues(ids));
+        } else {
+            step = new GraphStep<>(this.asAdmin(), Vertex.class, false, ids);
+        }
+
+        return this.asAdmin().addStep(step);
     }
 
     /**
@@ -411,14 +430,16 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     public default GraphTraversal<S, Edge> E(final Object... edgeIdsOrElements) {
         // a single null is [null]
         final Object[] ids = null == edgeIdsOrElements ? new Object[] { null } : edgeIdsOrElements;
-        // convert any vertices to ids
-        for (int i = 0; i < ids.length; i++) {
-            if (ids[i] instanceof Edge) {
-                ids[i] = ((Edge) ids[i]).id();
-            }
-        }
         this.asAdmin().getGremlinLang().addStep(Symbols.E, ids);
-        return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Edge.class, false, ids));
+
+        GraphStepContract<E, Edge> step;
+        if (GValue.containsGValues(ids)) {
+            step = new GraphStepPlaceholder<>(this.asAdmin(), Edge.class, false, GValue.ensureGValues(ids));
+        } else {
+            step = new GraphStep<>(this.asAdmin(), Edge.class, false, ids);
+        }
+
+        return this.asAdmin().addStep(step);
     }
 
     /**
@@ -455,11 +476,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> to(final Direction direction, final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return to(direction);
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.to, direction, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, direction, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Vertex.class, direction, edgeLabels));
     }
 
     /**
@@ -467,7 +491,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      *
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> out() {
         return this.out(new String[0]);
@@ -493,11 +517,15 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.7.3
+     * @since 3.8.0
      */
+
     public default GraphTraversal<S, Vertex> out(final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return out();
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.out, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.OUT, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Vertex.class, Direction.OUT, edgeLabels));
     }
 
     /**
@@ -505,7 +533,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      *
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> in() {
         return this.in(new String[0]);
@@ -532,11 +560,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> in(final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return in();
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.in, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.IN, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Vertex.class, Direction.IN, edgeLabels));
     }
 
     /**
@@ -544,7 +575,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      *
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> both() {
         return this.both(new String[0]);
@@ -570,11 +601,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> both(final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return both();
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.both, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.BOTH, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Vertex.class, Direction.BOTH, edgeLabels));
     }
 
     /**
@@ -583,7 +617,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param direction  the direction to traverse from the current vertex
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> toE(final Direction direction) {
         return this.toE(direction, new String[0]);
@@ -612,11 +646,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> toE(final Direction direction, final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return toE(direction);
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.toE, direction, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, direction, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Edge.class, direction, edgeLabels));
     }
 
     /**
@@ -624,7 +661,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      *
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> outE() {
         return this.outE(new String[0]);
@@ -650,11 +687,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> outE(final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return outE();
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.outE, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.OUT, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Edge.class, Direction.OUT, edgeLabels));
     }
 
     /**
@@ -662,7 +702,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      *
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> inE() {
         return this.inE(new String[0]);
@@ -688,11 +728,15 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
+
     public default GraphTraversal<S, Edge> inE(final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return inE();
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.inE, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.IN, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Edge.class, Direction.IN, edgeLabels));
     }
 
     /**
@@ -700,7 +744,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      *
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 3.0.0-incubating
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> bothE() {
         return this.bothE(new String[0]);
@@ -726,11 +770,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabels the edge labels to traverse
      * @return the traversal with an appended {@link VertexStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> bothE(final GValue<String>... edgeLabels) {
+        if (edgeLabels.length == 0) {
+            return bothE();
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.bothE, edgeLabels);
-        return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.BOTH, edgeLabels));
+        return this.asAdmin().addStep(new VertexStepPlaceholder<>(this.asAdmin(), Edge.class, Direction.BOTH, edgeLabels));
     }
 
     /**
@@ -1346,7 +1393,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     public default GraphTraversal<S, Vertex> addV(final String vertexLabel) {
         if (null == vertexLabel) throw new IllegalArgumentException("vertexLabel cannot be null");
         this.asAdmin().getGremlinLang().addStep(Symbols.addV, vertexLabel);
-        return this.asAdmin().addStep(new AddVertexStep<>(this.asAdmin(), vertexLabel));
+        return this.asAdmin().addStep(new AddVertexStepPlaceholder<>(this.asAdmin(), vertexLabel));
     }
 
     /**
@@ -1359,7 +1406,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     public default GraphTraversal<S, Vertex> addV(final Traversal<?, String> vertexLabelTraversal) {
         if (null == vertexLabelTraversal) throw new IllegalArgumentException("vertexLabelTraversal cannot be null");
         this.asAdmin().getGremlinLang().addStep(Symbols.addV, vertexLabelTraversal);
-        return this.asAdmin().addStep(new AddVertexStep<>(this.asAdmin(), vertexLabelTraversal.asAdmin()));
+        return this.asAdmin().addStep(new AddVertexStepPlaceholder<>(this.asAdmin(), vertexLabelTraversal.asAdmin()));
     }
 
     /**
@@ -1368,12 +1415,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param vertexLabel the label of the {@link Vertex} to add
      * @return the traversal with the {@link AddVertexStep} added
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addvertex-step" target="_blank">Reference Documentation - AddVertex Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> addV(final GValue<String> vertexLabel) {
         if (null == vertexLabel || null == vertexLabel.get()) throw new IllegalArgumentException("vertexLabel cannot be null");
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.addV, vertexLabel);
-        return this.asAdmin().addStep(new AddVertexStep<>(this.asAdmin(), vertexLabel));
+        return this.asAdmin().addStep(new AddVertexStepPlaceholder<>(this.asAdmin(), vertexLabel));
     }
 
     /**
@@ -1398,8 +1445,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Vertex> mergeV() {
         this.asAdmin().getGremlinLang().addStep(Symbols.mergeV);
-        final MergeVertexStep<S> step = new MergeVertexStep<>(this.asAdmin(), false);
-        return this.asAdmin().addStep(step);
+        return this.asAdmin().addStep(new MergeVertexStepPlaceholder<>(this.asAdmin(), false));
     }
 
     /**
@@ -1413,8 +1459,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Vertex> mergeV(final Map<Object, Object> searchCreate) {
         this.asAdmin().getGremlinLang().addStep(Symbols.mergeV, searchCreate);
-        final MergeVertexStep<S> step = new MergeVertexStep<>(this.asAdmin(), false, searchCreate);
-        return this.asAdmin().addStep(step);
+        return this.asAdmin().addStep(new MergeVertexStepPlaceholder<>(this.asAdmin(), false, searchCreate));
     }
 
     /**
@@ -1429,8 +1474,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Vertex> mergeV(final Traversal<?, Map<Object, Object>> searchCreate) {
         this.asAdmin().getGremlinLang().addStep(Symbols.mergeV, searchCreate);
-        final MergeVertexStep<S> step = null == searchCreate ? new MergeVertexStep(this.asAdmin(), false, (Map) null) :
-                new MergeVertexStep(this.asAdmin(), false, searchCreate.asAdmin());
+        final MergeVertexStepPlaceholder<S> step = null == searchCreate ? new MergeVertexStepPlaceholder<>(this.asAdmin(), false, (Map) null) :
+                new MergeVertexStepPlaceholder<>(this.asAdmin(), false, searchCreate.asAdmin());
         return this.asAdmin().addStep(step);
     }
 
@@ -1441,11 +1486,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * criteria to create the new {@link Vertex}.
      *
      * @param searchCreate This {@code Map} can have a key of {@link T} or a {@code String}.
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) {
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.mergeV, searchCreate);
-        final MergeVertexStep<S> step = new MergeVertexStep(this.asAdmin(), false, null == searchCreate ? GValue.ofMap(null, null) : searchCreate);
+        final MergeVertexStepPlaceholder<S> step = null == searchCreate ? new MergeVertexStepPlaceholder<>(this.asAdmin(), false, (Map) null) :
+                new MergeVertexStepPlaceholder<>(this.asAdmin(), false, searchCreate);
         return this.asAdmin().addStep(step);
     }
 
@@ -1457,7 +1503,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Edge> mergeE() {
         this.asAdmin().getGremlinLang().addStep(Symbols.mergeE);
-        final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false);
+        final MergeEdgeStepPlaceholder<S> step = new MergeEdgeStepPlaceholder<>(this.asAdmin(), false);
         return this.asAdmin().addStep(step);
     }
 
@@ -1470,17 +1516,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Edge> mergeE(final Map<Object, Object> searchCreate) {
         // get a construction time exception if the Map is bad
-
-        // Replace any vertices with ids for purposes of GremlinLang
-        if (searchCreate != null && searchCreate.get(Direction.OUT) instanceof Vertex) {
-            searchCreate.put(Direction.OUT, ((Vertex) searchCreate.get(Direction.OUT)).id());
-        }
-        if (searchCreate != null && searchCreate.get(Direction.IN) instanceof Vertex) {
-            searchCreate.put(Direction.IN, ((Vertex) searchCreate.get(Direction.IN)).id());
-        }
         this.asAdmin().getGremlinLang().addStep(Symbols.mergeE, searchCreate);
-
-        final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false, searchCreate);
+        final MergeEdgeStepPlaceholder<S> step = new MergeEdgeStepPlaceholder<>(this.asAdmin(), false, searchCreate);
         return this.asAdmin().addStep(step);
     }
 
@@ -1494,8 +1531,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     public default GraphTraversal<S, Edge> mergeE(final Traversal<?, Map<Object, Object>> searchCreate) {
         this.asAdmin().getGremlinLang().addStep(Symbols.mergeE, searchCreate);
 
-        final MergeEdgeStep<S> step = null == searchCreate ? new MergeEdgeStep(this.asAdmin(), false,  (Map) null) :
-                new MergeEdgeStep(this.asAdmin(), false, searchCreate.asAdmin());
+        final MergeEdgeStepPlaceholder<S> step = null == searchCreate ? new MergeEdgeStepPlaceholder<>(this.asAdmin(), false,  (Map) null) :
+                new MergeEdgeStepPlaceholder<>(this.asAdmin(), false, searchCreate.asAdmin());
         return this.asAdmin().addStep(step);
     }
 
@@ -1504,24 +1541,13 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * {@code Map} as an argument.
      *
      * @param searchCreate This {@code Map} can have a key of {@link T} {@link Direction} or a {@code String}.
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> mergeE(final GValue<Map<Object, Object>> searchCreate) {
         // get a construction time exception if the Map is bad
-
-        // Replace any vertices with ids for purposes of GremlinLang
-        if (searchCreate != null && !searchCreate.isNull()) {
-            Map<Object, Object> searchCreateMap = searchCreate.get();
-            if (searchCreateMap != null && searchCreateMap.get(Direction.OUT) instanceof Vertex) {
-                searchCreateMap.put(Direction.OUT, ((Vertex) searchCreateMap.get(Direction.OUT)).id());
-            }
-            if (searchCreateMap != null && searchCreateMap.get(Direction.IN) instanceof Vertex) {
-                searchCreateMap.put(Direction.IN, ((Vertex) searchCreateMap.get(Direction.IN)).id());
-            }
-        }
-
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.mergeE, searchCreate);
-        final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false, null == searchCreate ? GValue.ofMap(null, null) : searchCreate);
+        final MergeEdgeStepPlaceholder<S> step = null == searchCreate ? new MergeEdgeStepPlaceholder<>(this.asAdmin(), false,  (Map) null) :
+                new MergeEdgeStepPlaceholder<>(this.asAdmin(), false, searchCreate);
         return this.asAdmin().addStep(step);
     }
 
@@ -1535,7 +1561,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Edge> addE(final String edgeLabel) {
         this.asAdmin().getGremlinLang().addStep(Symbols.addE, edgeLabel);
-        return this.asAdmin().addStep(new AddEdgeStep<>(this.asAdmin(), edgeLabel));
+        // Use AddEdgeStepGValueContract even if label is not GValue in case `from(), `to()`, or `property()` steps with GValue are merged in.
+        return this.asAdmin().addStep(new AddEdgeStepPlaceholder<>(this.asAdmin(), edgeLabel));
     }
 
     /**
@@ -1547,7 +1574,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, Edge> addE(final Traversal<?, String> edgeLabelTraversal) {
         this.asAdmin().getGremlinLang().addStep(Symbols.addE, edgeLabelTraversal);
-        return this.asAdmin().addStep(new AddEdgeStep<>(this.asAdmin(), null == edgeLabelTraversal ? null : edgeLabelTraversal.asAdmin()));
+        // Use AddEdgeStepGValueContract even if label is not GValue in case `from(), `to()`, or `property()` steps with GValue are merged in.
+        return this.asAdmin().addStep(new AddEdgeStepPlaceholder<>(this.asAdmin(), null == edgeLabelTraversal ? null : edgeLabelTraversal.asAdmin()));
     }
 
     /**
@@ -1556,49 +1584,70 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param edgeLabel the label of the newly added edge
      * @return the traversal with the {@link AddEdgeStep} added
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - AddEdge Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, Edge> addE(final GValue<String> edgeLabel) {
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.addE, edgeLabel);
-        return this.asAdmin().addStep(new AddEdgeStep<>(this.asAdmin(), edgeLabel));
+        AddEdgeStepPlaceholder<Edge> step = new AddEdgeStepPlaceholder<>(this.asAdmin(), edgeLabel);
+        return this.asAdmin().addStep(step);
+    }
+
+    /**
+     * Provide {@code from()}-modulation to respective steps.
+     *
+     * @param fromStepLabel the step label to modulate to.
+     * @return the traversal with the modified {@link FromToModulating} step.
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#from-step" target="_blank">Reference Documentation - From Step</a>
+     * @since 3.1.0-incubating
+     */
+    public default GraphTraversal<S, E> from(final String fromStepLabel) {
+        final Step<?,?> prev = this.asAdmin().getEndStep();
+        if (!(prev instanceof FromToModulating))
+            throw new IllegalArgumentException(String.format(
+                    "The from() step cannot follow %s", prev.getClass().getSimpleName()));
+
+        this.asAdmin().getGremlinLang().addStep(Symbols.from, fromStepLabel);
+        ((FromToModulating) prev).addFrom(fromStepLabel);
+        return this;
     }
 
     /**
      * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
      * outgoing vertex of the newly added {@link Edge}.
      *
-     * @param fromVertex the traversal for selecting the outgoing vertex
+     * @param fromVertex the vertex for selecting the outgoing vertex
      * @return the traversal with the modified {@link AddEdgeStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
-     * @since 3.1.0-incubating
+     * @since 3.8.0
      */
-    public default GraphTraversal<S, E> from(final Traversal<?, Object> fromVertex) {
+    public default GraphTraversal<S, E> from(final GValue<Vertex> fromVertex) {
         final Step<?,?> prev = this.asAdmin().getEndStep();
         if (!(prev instanceof FromToModulating))
             throw new IllegalArgumentException(String.format(
                     "The from() step cannot follow %s", prev.getClass().getSimpleName()));
 
         this.asAdmin().getGremlinLang().addStep(Symbols.from, fromVertex);
-        ((FromToModulating) prev).addFrom(fromVertex.asAdmin());
+        ((FromToModulating) prev).addFrom(new GValueConstantTraversal<S, Vertex>(fromVertex));
         return this;
     }
 
     /**
-     * Provide {@code from()}-modulation to respective steps.
+     * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
+     * outgoing vertex of the newly added {@link Edge}.
      *
-     * @param fromStepLabel the step label to modulate to.
-     * @return the traversal with the modified {@link FromToModulating} step.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#from-step" target="_blank">Reference Documentation - From Step</a>
-     * @since 3.1.0-incubating
+     * @param fromVertex the vertex for selecting the outgoing vertex
+     * @return the traversal with the modified {@link AddEdgeStep}
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
+     * @since 3.3.0
      */
-    public default GraphTraversal<S, E> from(final String fromStepLabel) {
+    public default GraphTraversal<S, E> from(final Vertex fromVertex) {
         final Step<?,?> prev = this.asAdmin().getEndStep();
         if (!(prev instanceof FromToModulating))
             throw new IllegalArgumentException(String.format(
                     "The from() step cannot follow %s", prev.getClass().getSimpleName()));
 
-        this.asAdmin().getGremlinLang().addStep(Symbols.from, fromStepLabel);
-        ((FromToModulating) prev).addFrom(fromStepLabel);
+        this.asAdmin().getGremlinLang().addStep(Symbols.from, fromVertex);
+        ((FromToModulating) prev).addFrom(__.constant(fromVertex).asAdmin());
         return this;
     }
 
@@ -1606,27 +1655,39 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
      * outgoing vertex of the newly added {@link Edge}.
      *
-     * @param fromVertex the vertex for selecting the outgoing vertex
+     * @param fromVertex the traversal for selecting the outgoing vertex
      * @return the traversal with the modified {@link AddEdgeStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
-     * @since 4.0.0
+     * @since 3.1.0-incubating
      */
-    public default GraphTraversal<S, E> from(final GValue<Object> fromVertexOrId) {
+    public default GraphTraversal<S, E> from(final Traversal<?, Object> fromVertex) {
         final Step<?,?> prev = this.asAdmin().getEndStep();
         if (!(prev instanceof FromToModulating))
             throw new IllegalArgumentException(String.format(
                     "The from() step cannot follow %s", prev.getClass().getSimpleName()));
 
-        // Convert vertex to id for the purposes of GremlinLang
-        GValue<Object> fromId;
-        if (fromVertexOrId.get() instanceof Vertex) {
-            fromId = GValue.of(fromVertexOrId.getName(), ((Vertex) fromVertexOrId.get()).id());
-        } else {
-            fromId = fromVertexOrId;
-        }
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.from, fromId);
+        this.asAdmin().getGremlinLang().addStep(Symbols.from, fromVertex);
+        ((FromToModulating) prev).addFrom(fromVertex.asAdmin());
+        return this;
+    }
 
-        ((FromToModulating) prev).addFrom(__.constant(fromId).asAdmin());
+    /**
+     * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
+     * incoming vertex of the newly added {@link Edge}.
+     *
+     * @param toVertex the vertex for selecting the incoming vertex
+     * @return the traversal with the modified {@link AddEdgeStep}
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
+     * @since 3.8.0
+     */
+    public default GraphTraversal<S, E> to(final GValue<Vertex> toVertex) {
+        final Step<?,?> prev = this.asAdmin().getEndStep();
+        if (!(prev instanceof FromToModulating))
+            throw new IllegalArgumentException(String.format(
+                    "The to() step cannot follow %s", prev.getClass().getSimpleName()));
+        
+        this.asAdmin().getGremlinLang().addStep(Symbols.to, toVertex);
+        ((FromToModulating) prev).addTo(new GValueConstantTraversal<S, Vertex>(toVertex));
         return this;
     }
 
@@ -1654,7 +1715,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         // Convert vertex to id for the purposes of GremlinLang
         Object fromId = fromVertexOrId instanceof Vertex ? ((Vertex) fromVertexOrId).id() : fromVertexOrId;
         this.asAdmin().getGremlinLang().addStep(Symbols.from, fromId);
-        ((FromToModulating) prev).addFrom(__.constant(fromId).asAdmin());
+        ((FromToModulating) prev).addFrom(fromVertexOrId instanceof GValue ?
+                new GValueConstantTraversal<>((GValue<Object>) fromVertexOrId) :
+                __.constant(fromVertexOrId).asAdmin());
         return this;
     }
 
@@ -1677,34 +1740,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this;
     }
 
-    /**
-     * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
-     * incoming vertex of the newly added {@link Edge}.
-     *
-     * @param toVertexOrId the vertex for selecting the incoming vertex
-     * @return the traversal with the modified {@link AddEdgeStep}
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, E> to(final GValue<Object> toVertexOrId) {
-        final Step<?,?> prev = this.asAdmin().getEndStep();
-        if (!(prev instanceof FromToModulating))
-            throw new IllegalArgumentException(String.format(
-                    "The to() step cannot follow %s", prev.getClass().getSimpleName()));
-
-        // Convert vertex to id for the purposes of GremlinLang
-        GValue<Object> toId;
-        if (toVertexOrId.get() instanceof Vertex) {
-            toId = GValue.of(toVertexOrId.getName(), ((Vertex) toVertexOrId.get()).id());
-        } else {
-            toId = toVertexOrId;
-        }
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.to, toId);
-
-        ((FromToModulating) prev).addTo(__.constant(toId).asAdmin());
-        return this;
-    }
-
     /**
      * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
      * incoming vertex of the newly added {@link Edge}.
@@ -1745,10 +1780,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         if (!(prev instanceof FromToModulating))
             throw new IllegalArgumentException(String.format(
                     "The to() step cannot follow %s", prev.getClass().getSimpleName()));
-
-        Object toId = toVertexOrId instanceof Vertex ? ((Vertex) toVertexOrId).id() : toVertexOrId;
-        this.asAdmin().getGremlinLang().addStep(Symbols.to, toId);
-        ((FromToModulating) prev).addTo(__.constant(toId).asAdmin());
+        
+        this.asAdmin().getGremlinLang().addStep(Symbols.to, toVertexOrId);
+        ((FromToModulating) prev).addTo(toVertexOrId instanceof GValue ?
+                new GValueConstantTraversal<>((GValue<Object>) toVertexOrId) :
+                __.constant(toVertexOrId).asAdmin());
         return this;
     }
 
@@ -1812,12 +1848,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param params static parameter map (no nested traversals)
      * @return the traversal with an appended {@link CallStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#call-step" target="_blank">Reference Documentation - Call Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    default <E> GraphTraversal<S, E> call(final String service, final GValue<Map> params) {
+    default <E> GraphTraversal<S, E> call(final String service, final GValue<Map<?,?>> params) {
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.call, service, params);
-        final CallStep<S,E> call = new CallStep<>(this.asAdmin(), false, service, params);
-        return this.asAdmin().addStep(call);
+        return this.asAdmin().addStep(new CallStepPlaceholder<>(this.asAdmin(), false, service, params));
     }
 
     /**
@@ -1865,12 +1900,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param childTraversal a traversal that will produce a Map of parameters for the service call when invoked.
      * @return the traversal with an appended {@link CallStep}.
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#call-step" target="_blank">Reference Documentation - Call Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    default <E> GraphTraversal<S, E> call(final String service, final GValue<Map> params, final Traversal<?, Map<?,?>> childTraversal) {
+    default <E> GraphTraversal<S, E> call(final String service, final GValue<Map<?,?>> params, final Traversal<S, Map<?,?>> childTraversal) {
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.call, service, params, childTraversal);
-        final CallStep<S,E> step = null == childTraversal ? new CallStep(this.asAdmin(), false, service, params) :
-                new CallStep(this.asAdmin(), false, service, params, childTraversal.asAdmin());
+        final CallStepPlaceholder<S,E> step = null == childTraversal ? new CallStepPlaceholder<>(this.asAdmin(), false, service, params) :
+                new CallStepPlaceholder<>(this.asAdmin(), false, service, params, childTraversal.asAdmin());
         return this.asAdmin().addStep(step);
     }
 
@@ -2374,18 +2409,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new DifferenceStep<>(this.asAdmin(), values));
     }
 
-    /**
-     * Calculates the difference between the list traverser and list argument.
-     *
-     * @return the traversal with an appended {@link DifferenceStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#difference-step" target="_blank">Reference Documentation - Difference Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, Set<?>> difference(final GValue<Object> values) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.difference, values);
-        return this.asAdmin().addStep(new DifferenceStep<>(this.asAdmin(), values));
-    }
-
     /**
      * Calculates the disjunction between the list traverser and list argument.
      *
@@ -2398,18 +2421,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new DisjunctStep<>(this.asAdmin(), values));
     }
 
-    /**
-     * Calculates the disjunction between the list traverser and list argument.
-     *
-     * @return the traversal with an appended {@link DisjunctStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#disjunct-step" target="_blank">Reference Documentation - Disjunct Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, Set<?>> disjunct(final GValue<Object> values) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.disjunct, values);
-        return this.asAdmin().addStep(new DisjunctStep<>(this.asAdmin(), values));
-    }
-
     /**
      * Calculates the intersection between the list traverser and list argument.
      *
@@ -2422,18 +2433,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new IntersectStep<>(this.asAdmin(), values));
     }
 
-    /**
-     * Calculates the intersection between the list traverser and list argument.
-     *
-     * @return the traversal with an appended {@link IntersectStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#intersect-step" target="_blank">Reference Documentation - Intersect Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, Set<?>> intersect(final GValue<Object> values) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.intersect, values);
-        return this.asAdmin().addStep(new IntersectStep<>(this.asAdmin(), values));
-    }
-
     /**
      * Joins together the elements of the incoming list traverser together with the provided delimiter.
      *
@@ -2446,18 +2445,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new ConjoinStep<>(this.asAdmin(), delimiter));
     }
 
-    /**
-     * Joins together the elements of the incoming list traverser together with the provided delimiter.
-     *
-     * @return the traversal with an appended {@link ConjoinStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#conjoin-step" target="_blank">Reference Documentation - Conjoin Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, String> conjoin(final GValue<String> delimiter) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.conjoin, delimiter);
-        return this.asAdmin().addStep(new ConjoinStep<>(this.asAdmin(), delimiter));
-    }
-
     /**
      * Merges the list traverser and list argument. Also known as union.
      *
@@ -2470,18 +2457,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new MergeStep<>(this.asAdmin(), values));
     }
 
-    /**
-     * Merges the list traverser and list argument. Also known as union.
-     *
-     * @return the traversal with an appended {@link MergeStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#merge-step" target="_blank">Reference Documentation - Merge Step</a>
-     * @since 4.0.0
-     */
-    public default <E2> GraphTraversal<S, E2> merge(final GValue<Object> values) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.merge, values);
-        return this.asAdmin().addStep(new MergeStep<>(this.asAdmin(), values));
-    }
-
     /**
      * Combines the list traverser and list argument. Also known as concatenation or append.
      *
@@ -2494,18 +2469,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new CombineStep<>(this.asAdmin(), values));
     }
 
-    /**
-     * Combines the list traverser and list argument. Also known as concatenation or append.
-     *
-     * @return the traversal with an appended {@link CombineStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#combine-step" target="_blank">Reference Documentation - Combine Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, List<?>> combine(final GValue<Object> values) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.combine, values);
-        return this.asAdmin().addStep(new CombineStep<>(this.asAdmin(), values));
-    }
-
     /**
      * Calculates the cartesian product between the list traverser and list argument.
      *
@@ -2518,18 +2481,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new ProductStep<>(this.asAdmin(), values));
     }
 
-    /**
-     * Calculates the cartesian product between the list traverser and list argument.
-     *
-     * @return the traversal with an appended {@link ProductStep}.
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#product-step" target="_blank">Reference Documentation - Product Step</a>
-     * @since 4.0.0
-     */
-    public default GraphTraversal<S, List<List<?>>> product(final GValue<Object> values) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.product, values);
-        return this.asAdmin().addStep(new ProductStep<>(this.asAdmin(), values));
-    }
-
     ///////////////////// FILTER STEPS /////////////////////
 
     /**
@@ -2542,6 +2493,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.0.0-incubating
      */
     public default GraphTraversal<S, E> filter(final Predicate<Traverser<E>> predicate) {
+        if (predicate != null && predicate instanceof P && ((P<Traverser<E>>) predicate).isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by filter()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.filter, predicate);
         return this.asAdmin().addStep(new LambdaFilterStep<>(this.asAdmin(), predicate));
     }
@@ -2651,6 +2605,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.0.0-incubating
      */
     public default GraphTraversal<S, E> where(final String startKey, final P<String> predicate) {
+        if (predicate != null && predicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by where()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.where, startKey, predicate);
         return this.asAdmin().addStep(new WherePredicateStep<>(this.asAdmin(), Optional.ofNullable(startKey), predicate));
     }
@@ -2666,6 +2623,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.0.0-incubating
      */
     public default GraphTraversal<S, E> where(final P<String> predicate) {
+        if (predicate != null && predicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by where()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.where, predicate);
         return this.asAdmin().addStep(new WherePredicateStep<>(this.asAdmin(), Optional.empty(), predicate));
     }
@@ -2875,7 +2835,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param value       the value to compare the accessor value to for equality
      * @return the traversal with an appended {@link HasStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> has(final GValue<String> label, final String propertyKey, final Object value) {
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.has, label.get(), propertyKey, value);
@@ -2891,7 +2851,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param predicate   the filter to apply to the key's value
      * @return the traversal with an appended {@link HasStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> has(final GValue<String> label, final String propertyKey, final P<?> predicate) {
         this.asAdmin().getGremlinLang().addStep(Symbols.has, label.get(), propertyKey, predicate);
@@ -2956,7 +2916,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param otherLabels additional labels of the {@link Element}
      * @return the traversal with an appended {@link HasStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> hasLabel(final GValue<String> label, final GValue<String>... otherLabels) {
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.hasLabel, label, otherLabels);
@@ -3002,7 +2962,13 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
                 // as ids are unrolled when it's in array, they should also be unrolled when it's a list.
                 // this also aligns with behavior of hasId() when it's pushed down to g.V() (TINKERPOP-2863)
                 ids.addAll((Collection<?>) id);
-            } else {
+            } else if (id instanceof GValue && ((GValue<?>) id).get() instanceof Object[]) {
+                // If a GValue is passed in containing an array or Collection, it is unrolled similarly to literal
+                // arguments. This comes at the expense of losing the GValue wrappings, but it necessary for consistency
+                Collections.addAll(ids, (Object[]) ((GValue<?>) id).get());
+            } else if (id instanceof GValue && ((GValue<?>) id).get() instanceof Collection) {
+                ids.addAll((Collection<?>) ((GValue<?>) id).get());
+            }else {
                 ids.add(id);
             }
 
@@ -3010,22 +2976,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
             // be turned into array first
             if (otherIds != null) {
                 for (final Object i : otherIds) {
-                    // to retain existing behavior, GValue's containing collections are unrolled by 1 layer.
-                    // For example, GValue.of([1, 2]) is processed to [GValue.of(1), GValue.of(2)]
-                    if(i instanceof GValue) {
-                        Object value = ((GValue) i).get();
-                        if (value instanceof Object[]) {
-                            ids.addAll(Arrays.asList(GValue.ensureGValues((Object[]) value)));
-                        } else if(value instanceof Collection) {
-                            ids.addAll(Arrays.asList(GValue.ensureGValues(((Collection<?>) value).toArray())));
-                        } else {
-                            ids.add(i);
-                        }
-                    }
-                    else if (i instanceof Object[]) {
+                    if (i instanceof Object[]) {
                         Collections.addAll(ids, (Object[]) i);
                     } else if (i instanceof Collection) {
                         ids.addAll((Collection<?>) i);
+                    } else if (i instanceof GValue && ((GValue<?>) i).get() instanceof Object[]) {
+                        Collections.addAll(ids, (Object[]) ((GValue<?>) i).get());
+                    } else if (i instanceof GValue && ((GValue<?>) i).get() instanceof Collection) {
+                        ids.addAll((Collection<?>) ((GValue<?>) i).get());
                     } else
                         ids.add(i);
                 }
@@ -3090,6 +3048,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         // if calling hasKey(null), the likely use the caller is going for is not a "no predicate" but a eq(null)
         if (null == predicate) {
             return hasKey((String) null);
+        } else if (predicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by hasKey()");
         } else {
             this.asAdmin().getGremlinLang().addStep(Symbols.hasKey, predicate);
             return TraversalHelper.addHasContainer(this.asAdmin(), new HasContainer(T.key.getAccessor(), predicate));
@@ -3160,7 +3120,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, E> is(final P<E> predicate) {
         this.asAdmin().getGremlinLang().addStep(Symbols.is, predicate);
-        return this.asAdmin().addStep(new IsStep<>(this.asAdmin(), predicate));
+
+        return this.asAdmin().addStep(predicate.isParameterized() ? new IsStepPlaceholder<>(this.asAdmin(), predicate) : new IsStep<>(this.asAdmin(), predicate));
     }
 
     /**
@@ -3173,7 +3134,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default GraphTraversal<S, E> is(final Object value) {
         this.asAdmin().getGremlinLang().addStep(Symbols.is, value);
-        return this.asAdmin().addStep(new IsStep<>(this.asAdmin(), value instanceof P ? (P<E>) value : P.eq((E) value)));
+        P<E> predicate = value instanceof P ? (P<E>) value : P.eq((E) value);
+        return this.asAdmin().addStep(predicate.isParameterized() ? new IsStepPlaceholder<>(this.asAdmin(), predicate) : new IsStep<>(this.asAdmin(), predicate));
     }
 
     /**
@@ -3238,11 +3200,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param high the number at which to end the stream - use {@code -1} to emit all remaining objects
      * @return the traversal with an appended {@link RangeGlobalStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#range-step" target="_blank">Reference Documentation - Range Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> range(final GValue<Long> low, final GValue<Long> high) {
         this.asAdmin().getGremlinLang().addStep(Symbols.range, low, high);
-        return this.asAdmin().addStep(new RangeGlobalStep<>(this.asAdmin(), low, high));
+        return this.asAdmin().addStep(new RangeGlobalStepPlaceholder(this.asAdmin(), low, high));
     }
 
     /**
@@ -3274,13 +3236,13 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param high  the number at which to end the stream - use {@code -1} to emit all remaining objects
      * @return the traversal with an appended {@link RangeGlobalStep} or {@link RangeLocalStep} depending on {@code scope}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#range-step" target="_blank">Reference Documentation - Range Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default <E2> GraphTraversal<S, E2> range(final Scope scope, final GValue<Long> low, final GValue<Long> high) {
         this.asAdmin().getGremlinLang().addStep(Symbols.range, scope, low, high);
         return this.asAdmin().addStep(scope.equals(Scope.global)
-                ? new RangeGlobalStep<>(this.asAdmin(), low, high)
-                : new RangeLocalStep<>(this.asAdmin(), low, high));
+                ? new RangeGlobalStepPlaceholder<>(this.asAdmin(), low, high)
+                : new RangeLocalStepPlaceholder<>(this.asAdmin(), low, high));
     }
 
     /**
@@ -3304,11 +3266,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param limit the number at which to end the stream
      * @return the traversal with an appended {@link RangeGlobalStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#limit-step" target="_blank">Reference Documentation - Limit Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> limit(final GValue<Long> limit) {
         this.asAdmin().getGremlinLang().addStep(Symbols.limit, limit);
-        return this.asAdmin().addStep(new RangeGlobalStep<>(this.asAdmin(), GValue.ofLong(null, 0L), limit));
+        return this.asAdmin().addStep(new RangeGlobalStepPlaceholder<>(this.asAdmin(), GValue.of(null, 0l), limit));
     }
 
     /**
@@ -3336,13 +3298,13 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param limit the number at which to end the stream
      * @return the traversal with an appended {@link RangeGlobalStep} or {@link RangeLocalStep} depending on {@code scope}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#limit-step" target="_blank">Reference Documentation - Limit Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default <E2> GraphTraversal<S, E2> limit(final Scope scope, final GValue<Long> limit) {
         this.asAdmin().getGremlinLang().addStep(Symbols.limit, scope, limit);
         return this.asAdmin().addStep(scope.equals(Scope.global)
-                ? new RangeGlobalStep<>(this.asAdmin(), GValue.ofLong(null, 0L), limit)
-                : new RangeLocalStep<>(this.asAdmin(), GValue.ofLong(null, 0L), limit));
+                ? new RangeGlobalStepPlaceholder<>(this.asAdmin(), GValue.of(null, 0l), limit)
+                : new RangeLocalStepPlaceholder<>(this.asAdmin(), GValue.of(null, 0l), limit));
     }
 
     /**
@@ -3379,11 +3341,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param limit the number at which to end the stream
      * @return the traversal with an appended {@link TailGlobalStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#tail-step" target="_blank">Reference Documentation - Tail Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> tail(final GValue<Long> limit) {
         this.asAdmin().getGremlinLang().addStep(Symbols.tail, limit);
-        return this.asAdmin().addStep(new TailGlobalStep<>(this.asAdmin(), limit));
+        return this.asAdmin().addStep(new TailGlobalStepPlaceholder<>(this.asAdmin(), limit));
     }
 
     /**
@@ -3427,13 +3389,15 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param limit the number at which to end the stream
      * @return the traversal with an appended {@link TailGlobalStep} or {@link TailLocalStep} depending on {@code scope}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#tail-step" target="_blank">Reference Documentation - Tail Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default <E2> GraphTraversal<S, E2> tail(final Scope scope, final GValue<Long> limit) {
         this.asAdmin().getGremlinLang().addStep(Symbols.tail, scope, limit);
-        return this.asAdmin().addStep(scope.equals(Scope.global)
-                ? new TailGlobalStep<>(this.asAdmin(), limit)
-                : new TailLocalStep<>(this.asAdmin(), limit));
+
+        Step<?, E2> step = scope.equals(Scope.global)
+                ? new TailGlobalStepPlaceholder<>(this.asAdmin(), limit)
+                : new TailLocalStepPlaceholder<>(this.asAdmin(), limit);
+        return this.asAdmin().addStep(step);
     }
 
     /**
@@ -3455,11 +3419,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param skip the number of objects to skip
      * @return the traversal with an appended {@link RangeGlobalStep}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#skip-step" target="_blank">Reference Documentation - Skip Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default GraphTraversal<S, E> skip(final GValue<Long> skip) {
         this.asAdmin().getGremlinLang().addStep(Symbols.skip, skip);
-        return this.asAdmin().addStep(new RangeGlobalStep<>(this.asAdmin(), skip, GValue.ofLong(null, -1L)));
+        return this.asAdmin().addStep(new RangeGlobalStepPlaceholder<>(this.asAdmin(), skip, GValue.of(null, -1l)));
     }
 
     /**
@@ -3485,13 +3449,14 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @param skip  the number of objects to skip
      * @return the traversal with an appended {@link RangeGlobalStep} or {@link RangeLocalStep} depending on {@code scope}
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#skip-step" target="_blank">Reference Documentation - Skip Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public default <E2> GraphTraversal<S, E2> skip(final Scope scope, final GValue<Long> skip) {
         this.asAdmin().getGremlinLang().addStep(Symbols.skip, scope, skip);
+
         return this.asAdmin().addStep(scope.equals(Scope.global)
-                ? new RangeGlobalStep<>(this.asAdmin(), skip, GValue.ofLong(null, -1L))
-                : new RangeLocalStep<>(this.asAdmin(), skip, GValue.ofLong(null, -1L)));
+                ? new RangeGlobalStepPlaceholder<>(this.asAdmin(), skip, GValue.of(null, -1l))
+                : new RangeLocalStepPlaceholder<>(this.asAdmin(), skip, GValue.of(null, -1l)));
     }
 
     /**
@@ -3583,6 +3548,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.7.1
      */
     public default <S2> GraphTraversal<S, E> all(final P<S2> predicate) {
+        if (predicate != null && predicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by all()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.all, predicate);
         return this.asAdmin().addStep(new AllStep<>(this.asAdmin(), predicate));
     }
@@ -3596,6 +3564,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.7.1
      */
     public default <S2> GraphTraversal<S, E> any(final P<S2> predicate) {
+        if (predicate != null && predicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by any()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.any, predicate);
         return this.asAdmin().addStep(new AnyStep<>(this.asAdmin(), predicate));
     }
@@ -3845,6 +3816,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     public default GraphTraversal<S, E> property(final VertexProperty.Cardinality cardinality, final Object key, final Object value, final Object... keyValues) {
         if (key instanceof T && null == value)
             throw new IllegalArgumentException("Value of T cannot be null");
+        if (key instanceof GValue) {
+            throw new IllegalArgumentException("GValue is not permitted for property keys");
+        }
+        if (keyValues.length % 2 != 0) {
+            throw new IllegalArgumentException("keyValues must contain key/value pairs, found key without matching value");
+        }
 
         if (null == cardinality)
             this.asAdmin().getGremlinLang().addStep(Symbols.property, key, value, keyValues);
@@ -3859,20 +3836,20 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         // and as it so happens with T the value must be set by way of that approach otherwise you get an error.
         // it should be safe to execute this loop this way as we'll either hit an "AddElementStep" or an "EmptyStep".
         // if empty, it will just use the regular AddPropertyStep being tacked on to the end of the traversal as usual
-        while (endStep instanceof AddPropertyStep) {
+        while (endStep instanceof AddPropertyStepContract) {
             endStep = endStep.getPreviousStep();
         }
 
         // edge properties can always be folded as there are no cardinality/metaproperties. of course, if the
         // cardinality is specified as something other than single or null it would be confusing to simply allow it to
         // execute and not throw an error.
-        if ((endStep instanceof AddEdgeStep || endStep instanceof AddEdgeStartStep) && (null != cardinality && cardinality != single))
+        if ((endStep instanceof AddEdgeStepContract) && (null != cardinality && cardinality != single))
             throw new IllegalStateException(String.format(
                     "Multi-property cardinality of [%s] can only be set for a Vertex but is being used for addE() with key: %s",
                     cardinality.name(), key));
 
         // for a vertex mutation, it's possible to fold the property() into the Mutating step if there are no
-        // metaproperties (i.e. keyValues) and if (1) the key is an instance of T OR OR (3) the key is a string and the
+        // metaproperties (i.e. keyValues) and if (1) the key is an instance of T OR (2) the key is a string and the
         // cardinality is not specified. Note that checking for single cardinality of the argument doesn't work well
         // because once folded we lose the cardinality argument associated to the key/value pair and then it relies on
         // the graph. that means that if you do:
@@ -3889,15 +3866,16 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         // construction but during iteration. not folding to AddVertexStep creates different (breaking) traversal
         // semantics than we've had in previous versions so right/wrong could be argued, but since it's a breaking
         // change we'll just arbitrarily account for it to maintain the former behavior.
-        if ((endStep instanceof AddEdgeStep || endStep instanceof AddEdgeStartStep) ||
-                ((endStep instanceof AddVertexStep || endStep instanceof AddVertexStartStep) &&
-                  keyValues.length == 0 &&
+        if ((endStep instanceof AddEdgeStepContract) ||
+                ((endStep instanceof AddVertexStepContract) && keyValues.length == 0 &&
                   (key instanceof T || (key instanceof String && null == cardinality) || key instanceof Traversal))) {
-            ((Mutating) endStep).configure(key, value);
+            ((PropertyAdding) endStep).addProperty(key, value);
         } else {
-            final AddPropertyStep<Element> addPropertyStep = new AddPropertyStep<>(this.asAdmin(), cardinality, key, value);
+            final AddPropertyStepContract<Element> addPropertyStep = new AddPropertyStepPlaceholder<>(this.asAdmin(), cardinality, key, value);
             this.asAdmin().addStep(addPropertyStep);
-            addPropertyStep.configure(keyValues);
+            for (int i = 0; i < keyValues.length; i += 2) {
+                addPropertyStep.addProperty(keyValues[i], keyValues[i + 1]);
+            }
         }
         return this;
     }
@@ -4134,6 +4112,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default <E2> GraphTraversal<S, E2> choose(final P<E> choosePredicate,
                                                      final Traversal<?, E2> trueChoice, final Traversal<?, E2> falseChoice) {
+        if (choosePredicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by choose()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.choose, choosePredicate, trueChoice, falseChoice);
         return this.asAdmin().addStep(new ChooseStep<E, E2, Boolean>(this.asAdmin(), (Traversal.Admin<E, ?>) __.is(choosePredicate), (Traversal.Admin<E, E2>) trueChoice, (Traversal.Admin<E, E2>) falseChoice));
     }
@@ -4168,6 +4149,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      */
     public default <E2> GraphTraversal<S, E2> choose(final P<E> choosePredicate,
                                                      final Traversal<?, E2> trueChoice) {
+        if (choosePredicate.isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by choose()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.choose, choosePredicate, trueChoice);
         return this.asAdmin().addStep(new ChooseStep<E, E2, Boolean>(this.asAdmin(), (Traversal.Admin<E, ?>) __.is(choosePredicate), (Traversal.Admin<E, E2>) trueChoice, (Traversal.Admin<E, E2>) __.identity()));
     }
@@ -4261,6 +4245,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.0.0-incubating
      */
     public default GraphTraversal<S, E> emit(final Predicate<Traverser<E>> emitPredicate) {
+        if (emitPredicate != null && emitPredicate instanceof P && ((P<Traverser<E>>) emitPredicate).isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by emit()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.emit, emitPredicate);
         return RepeatStep.addEmitToTraversal(this, (Traversal.Admin<E, ?>) __.filter(emitPredicate));
     }
@@ -4299,6 +4286,9 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @since 3.0.0-incubating
      */
     public default GraphTraversal<S, E> until(final Predicate<Traverser<E>> untilPredicate) {
+        if (untilPredicate != null && untilPredicate instanceof P && ((P<Traverser<E>>) untilPredicate).isParameterized()) {
+            throw new IllegalArgumentException("Parameterized predicates are not supported by until()");
+        }
         this.asAdmin().getGremlinLang().addStep(Symbols.until, untilPredicate);
         return RepeatStep.addUntilToTraversal(this, (Traversal.Admin<E, ?>) __.filter(untilPredicate));
     }
@@ -4732,7 +4722,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         final Step<?, ?> lastStep = this.asAdmin().getEndStep();
 
         // CardinalityValueTraversal doesn't make sense for any prior step other than mergeV()
-        if (!(lastStep instanceof MergeVertexStep) && m != null) {
+        if (!(lastStep instanceof MergeVertexStep || lastStep instanceof MergeVertexStepPlaceholder) && m != null) {
             for (Object k : m.keySet()) {
                 final Object o = m.get(k);
                 if (o instanceof CardinalityValueTraversal)
@@ -4760,7 +4750,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         final Step<?, ?> lastStep = this.asAdmin().getEndStep();
 
         // CardinalityValueTraversal doesn't make sense for any prior step other than mergeV()
-        if (!(lastStep instanceof MergeVertexStep)) {
+        if (!(lastStep instanceof MergeVertexStep || lastStep instanceof MergeVertexStepPlaceholder)) {
             throw new IllegalStateException("option() with the Cardinality argument can only be used following mergeV()");
         }
 
@@ -4789,30 +4779,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this;
     }
 
-    /**
-     * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
-     * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
-     * the documentation of such steps to understand the usage context.
-     *
-     * @param token       the token that would trigger this option which may be a {@link Pick}, {@link Merge},
-     *                    a {@link Traversal}, {@link Predicate}, or object depending on the step being modulated.
-     * @param traversalOption the option as a traversal
-     * @return the traversal with the modulated step
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#choose-step" target="_blank">Reference Documentation - Choose Step</a>
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
-     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
-     * @since 4.0.0
-     */
-    public default <M, E2> GraphTraversal<S, E> option(final GValue<M> token, final Traversal<?, E2> traversalOption) {
-        this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.option, token, traversalOption);
-
-        // handle null similar to how option() with Map handles it, otherwise we get a NPE if this one gets used
-        final Traversal.Admin<E,E2> t = null == traversalOption ?
-                new ConstantTraversal<>(null) : (Traversal.Admin<E, E2>) traversalOption.asAdmin();
-        ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(token.get(), t);
-        return this;
-    }
-
     /**
      * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
      * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
@@ -4822,11 +4788,25 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @return the traversal with the modulated step
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    public default <M, E2> GraphTraversal<S, E> option(final M token, final GValue<Map<Object, Object>> m) {
+    public default <M, E2> GraphTraversal<S, E> option(final M token, final GValue<Map<?, ?>> m) {
+        if (m == null) {
+            return option(token, (Map) null);
+        }
         this.asAdmin().getGremlinLang().addStep(GraphTraversal.Symbols.option, token, m);
-        ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(token, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
+        TraversalOptionParent<M, E, E2> endStep = (TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep();
+        endStep.addChildOption(token, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m.get()).asAdmin());
+        if (endStep instanceof MergeStepContract) {
+            switch ((Merge) token) {
+                case onMatch:
+                    ((MergeStepContract) endStep).setOnMatch(new GValueConstantTraversal<>(m));
+                    break;
+                case onCreate:
+                    ((MergeStepContract) endStep).setOnCreate(new GValueConstantTraversal<>(m));
+                    break;
+            }
+        }
         return this;
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
index 968589b1db..9e1d350503 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
@@ -29,14 +29,17 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStepPlaceholder;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStepPlaceholder;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStepPlaceholder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStepPlaceholder;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IoStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStepContract;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -317,7 +320,7 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addV, vertexLabel);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddVertexStartStep(traversal, vertexLabel));
+        return traversal.addStep(new AddVertexStartStepPlaceholder(traversal, vertexLabel));
     }
 
     /**
@@ -331,7 +334,7 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addV, vertexLabelTraversal);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddVertexStartStep(traversal, vertexLabelTraversal));
+        return traversal.addStep(new AddVertexStartStepPlaceholder(traversal, vertexLabelTraversal.asAdmin()));
     }
 
     /**
@@ -343,21 +346,21 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addV);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddVertexStartStep(traversal, (String) null));
+        return traversal.addStep(new AddVertexStartStepPlaceholder(traversal, (String) null));
     }
 
     /**
      * Spawns a {@link GraphTraversal} by adding a vertex with the specified label. If the {@code label} is
      * {@code null} then it will default to {@link Vertex#DEFAULT_LABEL}.
      *
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public GraphTraversal<Vertex, Vertex> addV(final GValue<String> vertexLabel) {
         if (null == vertexLabel) throw new IllegalArgumentException("vertexLabel cannot be null");
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addV, vertexLabel);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddVertexStartStep(traversal, vertexLabel));
+        return traversal.addStep(new AddVertexStartStepPlaceholder(traversal, vertexLabel));
     }
 
     /**
@@ -369,7 +372,7 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addE, label);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddEdgeStartStep(traversal, label));
+        return traversal.addStep(new AddEdgeStartStepPlaceholder(traversal, label));
     }
 
     /**
@@ -381,19 +384,19 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addE, edgeLabelTraversal);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddEdgeStartStep(traversal, edgeLabelTraversal));
+        return traversal.addStep(new AddEdgeStartStepPlaceholder(traversal, edgeLabelTraversal.asAdmin()));
     }
 
     /**
      * Spawns a {@link GraphTraversal} by adding an edge with the specified label.
      *
-     * @since 4.0.0
+     * @since 3.8.0
      */
     public GraphTraversal<Edge, Edge> addE(final GValue<String> label) {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.addE, label);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new AddEdgeStartStep(traversal, label));
+        return traversal.addStep(new AddEdgeStartStepPlaceholder(traversal, label));
     }
 
     /**
@@ -409,7 +412,7 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.mergeV, searchCreate);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new MergeVertexStep(traversal, true, searchCreate));
+        return traversal.addStep(new MergeVertexStepPlaceholder<>(traversal, true, searchCreate));
     }
 
     /**
@@ -427,8 +430,8 @@ public class GraphTraversalSource implements TraversalSource {
         clone.gremlinLang.addStep(GraphTraversal.Symbols.mergeV, searchCreate);
         final GraphTraversal.Admin<S, Vertex> traversal = new DefaultGraphTraversal<>(clone);
 
-        final MergeVertexStep<S> step = null == searchCreate ? new MergeVertexStep(traversal, true, (Map) null) :
-                new MergeVertexStep(traversal, true, searchCreate.asAdmin());
+        final MergeVertexStepPlaceholder<S> step = null == searchCreate ? new MergeVertexStepPlaceholder(traversal, true, (Map) null) :
+                new MergeVertexStepPlaceholder(traversal, true, searchCreate.asAdmin());
 
         return traversal.addStep(step);
     }
@@ -440,13 +443,16 @@ public class GraphTraversalSource implements TraversalSource {
      * made it will use that search criteria to create the new {@link Vertex}.
      *
      * @param searchCreate This {@code Map} can have a key of {@link T} or a {@code String}.
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    public GraphTraversal<Vertex, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) {
+    public GraphTraversal<Vertex, Vertex> mergeV(final GValue<Map<?, ?>> searchCreate) {
         final GraphTraversalSource clone = GraphTraversalSource.this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.mergeV, searchCreate);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new MergeVertexStep(traversal, true, searchCreate));
+        final MergeVertexStepPlaceholder<Vertex> step = null == searchCreate ? new MergeVertexStepPlaceholder(traversal, true, (Map) null) :
+                new MergeVertexStepPlaceholder(traversal, true, searchCreate);
+
+        return traversal.addStep(step);
     }
 
     /**
@@ -456,11 +462,11 @@ public class GraphTraversalSource implements TraversalSource {
      * @param searchCreate This {@code Map} can have a key of {@link T} {@link Direction} or a {@code String}.
      * @since 3.6.0
      */
-    public GraphTraversal<Edge, Edge> mergeE(final Map<?, Object> searchCreate) {
+    public GraphTraversal<Edge, Edge> mergeE(final Map<Object, Object> searchCreate) {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.mergeE, searchCreate);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new MergeEdgeStep(traversal, true, searchCreate));
+        return traversal.addStep(new MergeEdgeStepPlaceholder<>(traversal, true, searchCreate));
     }
 
     /**
@@ -475,8 +481,8 @@ public class GraphTraversalSource implements TraversalSource {
         clone.gremlinLang.addStep(GraphTraversal.Symbols.mergeE, searchCreate);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
 
-        final MergeEdgeStep step = null == searchCreate ? new MergeEdgeStep(traversal, true,  (Map) null) :
-                new MergeEdgeStep(traversal, true, searchCreate.asAdmin());
+        final MergeEdgeStepPlaceholder step = null == searchCreate ? new MergeEdgeStepPlaceholder(traversal, true,  (Map) null) :
+                new MergeEdgeStepPlaceholder(traversal, true, searchCreate.asAdmin());
 
         return traversal.addStep(step);
     }
@@ -486,13 +492,16 @@ public class GraphTraversalSource implements TraversalSource {
      * {@code Map} as an argument.
      *
      * @param searchCreate This {@code Map} can have a key of {@link T} {@link Direction} or a {@code String}.
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    public GraphTraversal<Edge, Edge> mergeE(final GValue<Map<?, Object>> searchCreate) {
+    public GraphTraversal<Edge, Edge> mergeE(final GValue<Map<Object, Object>> searchCreate) {
         final GraphTraversalSource clone = GraphTraversalSource.this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.mergeE, searchCreate);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new MergeEdgeStep(traversal, true, searchCreate));
+        final MergeEdgeStepPlaceholder<Edge> step = null == searchCreate ? new MergeEdgeStepPlaceholder(traversal, true,  (Map) null) :
+                new MergeEdgeStepPlaceholder(traversal, true, searchCreate);
+
+        return traversal.addStep(step);
     }
 
     /**
@@ -519,7 +528,13 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.V, ids);
         final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new GraphStep<>(traversal, Vertex.class, true, ids));
+        GraphStepContract<Vertex, Vertex> step;
+        if (GValue.containsGValues(ids)) {
+            step = new GraphStepPlaceholder<>(traversal, Vertex.class, true, GValue.ensureGValues(ids));
+        } else {
+            step = new GraphStep<>(traversal, Vertex.class, true, ids);
+        }
+        return traversal.addStep(step);
     }
 
     /**
@@ -534,7 +549,13 @@ public class GraphTraversalSource implements TraversalSource {
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.E, ids);
         final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new GraphStep<>(traversal, Edge.class, true, ids));
+        GraphStepContract<Edge, Edge> step;
+        if (GValue.containsGValues(ids)) {
+            step = new GraphStepPlaceholder<>(traversal, Edge.class, true, GValue.ensureGValues(ids));
+        } else {
+            step = new GraphStep<>(traversal, Edge.class, true, ids);
+        }
+        return traversal.addStep(step);
     }
 
     /**
@@ -620,13 +641,13 @@ public class GraphTraversalSource implements TraversalSource {
      *
      * @param service the name of the service call
      * @param params static parameter map (no nested traversals)
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    public <S> GraphTraversal<S, S> call(final String service, final GValue<Map> params) {
+    public <S> GraphTraversal<S, S> call(final String service, final GValue<Map<?,?>> params) {
         final GraphTraversalSource clone = GraphTraversalSource.this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.call, service, params);
         final GraphTraversal.Admin<S, S> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new CallStep<>(traversal, true, service, params));
+        return traversal.addStep(new CallStepPlaceholder<>(traversal, true, service, params));
     }
 
     /**
@@ -635,13 +656,13 @@ public class GraphTraversalSource implements TraversalSource {
      *
      * @param service the name of the service call
      * @param params static parameter map (no nested traversals)
-     * @since 4.0.0
+     * @since 3.8.0
      */
-    public <S> GraphTraversal<S, S> call(final String service, final GValue<Map> params, final Traversal<S, Map> childTraversal) {
+    public <S> GraphTraversal<S, S> call(final String service, final GValue<Map<?,?>> params, final Traversal<S, Map<?,?>> childTraversal) {
         final GraphTraversalSource clone = GraphTraversalSource.this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.call, service, params, childTraversal);
         final GraphTraversal.Admin<S, S> traversal = new DefaultGraphTraversal<>(clone);
-        return traversal.addStep(new CallStep<>(traversal, true, service, params, childTraversal.asAdmin()));
+        return traversal.addStep(new CallStepPlaceholder<>(traversal, true, service, params, childTraversal.asAdmin()));
     }
 
     /**
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index 1ced17a2ea..14418cf8ec 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -985,13 +985,6 @@ public class __ {
         return __.<A>start().difference(values);
     }
 
-    /**
-     * @see GraphTraversal#difference(GValue)
-     */
-    public static <A> GraphTraversal<A, Set<?>> difference(final GValue<Object> values) {
-        return __.<A>start().difference(values);
-    }
-
     /**
      * @see GraphTraversal#discard()
      */
@@ -1006,13 +999,6 @@ public class __ {
         return __.<A>start().disjunct(values);
     }
 
-    /**
-     * @see GraphTraversal#disjunct(GValue)
-     */
-    public static <A> GraphTraversal<A, Set<?>> disjunct(final GValue<Object> values) {
-        return __.<A>start().disjunct(values);
-    }
-
     /**
      * @see GraphTraversal#intersect(Object)
      */
@@ -1020,13 +1006,6 @@ public class __ {
         return __.<A>start().intersect(values);
     }
 
-    /**
-     * @see GraphTraversal#intersect(GValue)
-     */
-    public static <A> GraphTraversal<A, Set<?>> intersect(final GValue<Object> values) {
-        return __.<A>start().intersect(values);
-    }
-
     /**
      * @see GraphTraversal#conjoin(String)
      */
@@ -1034,13 +1013,6 @@ public class __ {
         return __.<A>start().conjoin(values);
     }
 
-    /**
-     * @see GraphTraversal#conjoin(GValue)
-     */
-    public static <A> GraphTraversal<A, String> conjoin(final GValue<String> values) {
-        return __.<A>start().conjoin(values);
-    }
-
     /**
      * @see GraphTraversal#merge(Object)
      */
@@ -1048,13 +1020,6 @@ public class __ {
         return __.<A>start().merge(values);
     }
 
-    /**
-     * @see GraphTraversal#merge(GValue)
-     */
-    public static <A, B> GraphTraversal<A, B> merge(final GValue<Object> values) {
-        return __.<A>start().merge(values);
-    }
-
     /**
      * @see GraphTraversal#combine(Object)
      */
@@ -1062,13 +1027,6 @@ public class __ {
         return __.<A>start().combine(values);
     }
 
-    /**
-     * @see GraphTraversal#combine(GValue)
-     */
-    public static <A> GraphTraversal<A, List<?>> combine(final GValue<Object> values) {
-        return __.<A>start().combine(values);
-    }
-
     /**
      * @see GraphTraversal#product(Object)
      */
@@ -1076,13 +1034,6 @@ public class __ {
         return __.<A>start().product(values);
     }
 
-    /**
-     * @see GraphTraversal#product(GValue)
-     */
-    public static <A> GraphTraversal<A, List<List<?>>> product(final GValue<Object> values) {
-        return __.<A>start().product(values);
-    }
-
     ///////////////////// FILTER STEPS /////////////////////
 
     /**
@@ -1229,7 +1180,7 @@ public class __ {
     /**
      * @see GraphTraversal#hasLabel(GValue, GValue...)
      */
-    public static <A> GraphTraversal<A, A> hasLabel(final GValue<String> label, GValue<String>... otherLabels) {
+    public static <A> GraphTraversal<A, A> hasLabel(final GValue<String> label, final GValue<String>... otherLabels) {
         return __.<A>start().hasLabel(label, otherLabels);
     }
 
@@ -1861,7 +1812,7 @@ public class __ {
     /**
      * @see GraphTraversal#call(String, GValue)
      */
-    public static <A, B> GraphTraversal<A, B> call(final String service, final GValue<Map> params) {
+    public static <A, B> GraphTraversal<A, B> call(final String service, final GValue<Map<?,?>> params) {
         return __.<A>start().call(service, params);
     }
 
@@ -1882,7 +1833,7 @@ public class __ {
     /**
      * @see GraphTraversal#call(String, GValue, Traversal)
      */
-    public static <A, B> GraphTraversal<A, B> call(final String service, final GValue<Map> params, final Traversal<?, Map<?,?>> childTraversal) {
+    public static <A, B> GraphTraversal<A, B> call(final String service, final GValue<Map<?,?>> params, final Traversal<A, Map<?,?>> childTraversal) {
         return __.<A>start().call(service, params, childTraversal);
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/AbstractLambdaTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/AbstractLambdaTraversal.java
index 5aa089425b..314499f1f0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/AbstractLambdaTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/AbstractLambdaTraversal.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.lambda;
 
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
 import org.apache.tinkerpop.gremlin.process.traversal.GremlinLang;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -44,7 +45,7 @@ import java.util.Set;
 public abstract class AbstractLambdaTraversal<S, E> implements Traversal.Admin<S, E> {
 
     private static final Set<TraverserRequirement> REQUIREMENTS = Collections.singleton(TraverserRequirement.OBJECT);
-
+    protected GValueManager gValueManager = new GValueManager();
     protected Traversal.Admin<S, E> bypassTraversal = null;
 
     public void setBypassTraversal(final Traversal.Admin<S, E> bypassTraversal) {
@@ -65,6 +66,18 @@ public abstract class AbstractLambdaTraversal<S, E> implements Traversal.Admin<S
         return null == this.bypassTraversal ? new GremlinLang() : this.bypassTraversal.getGremlinLang();
     }
 
+    @Override
+    public GValueManager getGValueManager() {
+        return null == this.bypassTraversal ? gValueManager : this.bypassTraversal.getGValueManager();
+    }
+
+    @Override
+    public void setGValueManager(final GValueManager gValueManager) {
+        if (bypassTraversal == null)
+            this.gValueManager = gValueManager;
+        else
+            bypassTraversal.setGValueManager(gValueManager);
+    }
 
     @Override
     public void reset() {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/GValueConstantTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/GValueConstantTraversal.java
new file mode 100644
index 0000000000..a82d095fe7
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/GValueConstantTraversal.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.lambda;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+
+/**
+ * A {@link Traversal} that always returns a constant value.
+ */
+public final class GValueConstantTraversal<S, E> extends AbstractLambdaTraversal<S, E> {
+
+    private GValue<E> end;
+    private ConstantTraversal<S, E> constantTraversal;
+
+    public GValueConstantTraversal(final GValue<E> end) {
+        this.end = end == null ? GValue.of(null) : end;
+        this.constantTraversal = new ConstantTraversal<>(this.end.get());
+    }
+
+    @Override
+    public E next() {
+        return constantTraversal.next();
+    }
+
+    @Override
+    public String toString() {
+        return constantTraversal.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return this.getClass().hashCode() ^ end.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        if (other instanceof GValueConstantTraversal) {
+            return this.end.equals(((GValueConstantTraversal<?, ?>) other).end);
+        }
+        return constantTraversal.equals(other);
+    }
+
+    public ConstantTraversal<S, E> getConstantTraversal() {
+        return constantTraversal;
+    }
+
+    public boolean isParameterized() {
+        return end.isVariable();
+    }
+
+    public GValue<E> getGValue() {
+        return end;
+    }
+
+    public void updateVariable(String name, Object value) {
+        if (name.equals(end.getName())) {
+            end = (GValue<E>) GValue.of(name, value);
+            this.constantTraversal = new ConstantTraversal<>(end.get());
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
index 39a69ac633..769986fcdd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
@@ -18,17 +18,15 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.commons.lang3.SerializationException;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.structure.Edge;
-import org.apache.tinkerpop.gremlin.structure.Element;
-import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -39,24 +37,24 @@ import java.util.stream.Stream;
  * A {@code GValue} is a variable or literal value that is used in a {@link Traversal}. It is composed of a key-value
  * pair where the key is the name given to the variable and the value is the object that the variable resolved to. If
  * the name is not given, the value was provided literally in the traversal. The value of the variable can be any
- * object. The {@code GValue} also includes the {@link GType} that describes the type it contains.
+ * object.
  */
 public class GValue<V> implements Serializable {
     private final String name;
-    private final GType type;
-
     private final V value;
 
-    private GValue(final GType type, final V value) {
-        this(null, type, value);
+    private GValue(final V value) {
+        this(null, value);
     }
 
-    private GValue(final String name, final GType type, final V value) {
+    private GValue(final String name, final V value) {
         if (name != null && name.startsWith("_")) {
             throw new IllegalArgumentException(String.format("Invalid GValue name [%s]. Should not start with _.", name));
         }
+        if (value instanceof GValue) {
+            throw new IllegalArgumentException("GValues cannot be nested");
+        }
         this.name = name;
-        this.type = type;
         this.value = value;
     }
 
@@ -75,15 +73,6 @@ public class GValue<V> implements Serializable {
         return this.name;
     }
 
-    /**
-     * Gets the type of the value. The explicit type could be determined with {@code instanceof} on the value, but this
-     * might be helpful for cases where the value was constructed with a {@code null} value which might just return as
-     * {@code Object}.
-     */
-    public GType getType() {
-        return this.type;
-    }
-
     /**
      * Determines if the value held is of a {@code null} value.
      */
@@ -109,12 +98,32 @@ public class GValue<V> implements Serializable {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         final GValue<?> gValue = (GValue<?>) o;
-        return Objects.equals(name, gValue.name) && type == gValue.type && Objects.equals(value, gValue.value);
+        return Objects.equals(name, gValue.name) && Objects.equals(value, gValue.value);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(name, type, value);
+        return Objects.hash(name, value);
+    }
+
+    @Override
+    public GValue<V> clone() throws CloneNotSupportedException {
+        if (this.value == null) {
+            return new GValue<>(this.name, null);
+        }
+
+        if (this.value instanceof Serializable) {
+            try {
+                // attempt deep cloning
+                return new GValue<>(this.name, (V) SerializationUtils.clone((Serializable) this.value));
+            } catch (final SerializationException e) {
+                // Fall back to shallow clone if serialization fails
+                return new GValue<>(this.name, this.value);
+            }
+        }
+
+        // not serializable, fallback to shallow clone
+        return new GValue<>(this.name, this.value);
     }
 
     /**
@@ -123,9 +132,9 @@ public class GValue<V> implements Serializable {
      *
      * @param value the value of the variable
      */
-    static <V> GValue<V> of(final V value) {
+    public static <V> GValue<V> of(final V value) {
         if (value instanceof GValue) return (GValue) value;
-        return new GValue<>(GType.getType(value), value);
+        return new GValue<>(value);
     }
 
     /**
@@ -138,161 +147,161 @@ public class GValue<V> implements Serializable {
      */
     public static <V> GValue<V> of(final String name, final V value) {
         if (value instanceof GValue) throw new IllegalArgumentException("value cannot be a GValue");
-        return new GValue<>(name, GType.getType(value), value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a string value.
      */
-    static GValue<String> ofString(final String value) {
-        return new GValue<>(GType.STRING, value);
+    public static GValue<String> ofString(final String value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a string value with a specified name.
      */
     public static GValue<String> ofString(final String name, final String value) {
-        return new GValue<>(name, GType.STRING, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for an integer value.
      */
-    static GValue<Integer> ofInteger(final Integer value) {
-        return new GValue<>(GType.INTEGER, value);
+    public static GValue<Integer> ofInteger(final Integer value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for an integer value with a specified name.
      */
     public static GValue<Integer> ofInteger(final String name, final Integer value) {
-        return new GValue<>(name, GType.INTEGER, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a boolean value.
      */
-    static GValue<Boolean> ofBoolean(final Boolean value) {
-        return new GValue<>(GType.BOOLEAN, value);
+    public static GValue<Boolean> ofBoolean(final Boolean value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a boolean value with a specified name.
      */
     public static GValue<Boolean> ofBoolean(final String name, final Boolean value) {
-        return new GValue<>(name, GType.BOOLEAN, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a double value.
      */
-    static GValue<Double> ofDouble(final Double value) {
-        return new GValue<>(GType.DOUBLE, value);
+    public static GValue<Double> ofDouble(final Double value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a double value with a specified name.
      */
     public static GValue<Double> ofDouble(final String name, final Double value) {
-        return new GValue<>(name, GType.DOUBLE, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a BigInteger value.
      */
-    static GValue<BigInteger> ofBigInteger(final BigInteger value) {
-        return new GValue<>(GType.BIG_INTEGER, value);
+    public static GValue<BigInteger> ofBigInteger(final BigInteger value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a BigInteger value with a specified name.
      */
     public static GValue<BigInteger> ofBigInteger(final String name, final BigInteger value) {
-        return new GValue<>(name, GType.BIG_INTEGER, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a BigDecimal value.
      */
-    static GValue<BigDecimal> ofBigDecimal(final BigDecimal value) {
-        return new GValue<>(GType.BIG_DECIMAL, value);
+    public static GValue<BigDecimal> ofBigDecimal(final BigDecimal value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a BigDecimal value with a specified name.
      */
     public static GValue<BigDecimal> ofBigDecimal(final String name, final BigDecimal value) {
-        return new GValue<>(name, GType.BIG_DECIMAL, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a long value.
      */
-    static GValue<Long> ofLong(final Long value) {
-        return new GValue<>(GType.LONG, value);
+    public static GValue<Long> ofLong(final Long value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a long value with a specified name.
      */
     public static GValue<Long> ofLong(final String name, final Long value) {
-        return new GValue<>(name, GType.LONG, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a map value.
      */
-    static GValue<Map> ofMap(final Map value) {
-        return new GValue<>(GType.MAP, value);
+    public static GValue<Map> ofMap(final Map value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a map value with a specified name.
      */
-    public static GValue<Map> ofMap(final String name, final Map value) {
-        return new GValue<>(name, GType.MAP, value);
+    public static GValue<Map<?,?>> ofMap(final String name, final Map value) {
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a list value.
      */
-    static <T> GValue<List<T>> ofList(final List<T> value) {
-        return new GValue<>(GType.LIST, value);
+    public static <T> GValue<List<T>> ofList(final List<T> value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a list value with a specified name.
      */
     public static <T> GValue<List<T>> ofList(final String name, final List<T> value) {
-        return new GValue<>(name, GType.LIST, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a set value.
      */
-    static GValue<Set> ofSet(final Set value) {
-        return new GValue<>(GType.SET, value);
+    public static GValue<Set> ofSet(final Set value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a set value with a specified name.
      */
     public static GValue<Set> ofSet(final String name, final Set value) {
-        return new GValue<>(name, GType.SET, value);
+        return new GValue<>(name, value);
     }
 
     /**
      * Create a new {@code GValue} for a vertex value.
      */
-    static GValue<Vertex> ofVertex(final Vertex value) {
-        return new GValue<>(GType.VERTEX, value);
+    public static GValue<Vertex> ofVertex(final Vertex value) {
+        return new GValue<>(value);
     }
 
     /**
      * Create a new {@code GValue} for a vertex value with a specified name.
      */
     public static GValue<Vertex> ofVertex(final String name, final Vertex value) {
-        return new GValue<>(name, GType.VERTEX, value);
+        return new GValue<>(name, value);
     }
 
     /**
@@ -304,56 +313,10 @@ public class GValue<V> implements Serializable {
 
     /**
      * Tests if the object is a {@link GValue} and if so, checks the type of the value against the provided
-     * {@link GType}.
+     * {@link Class}.
      */
-    public static boolean valueInstanceOf(final Object o, final GType type) {
-        return o instanceof GValue && ((GValue) o).getType() == type;
-    }
-
-    /**
-     * Tests if the object is a {@link GValue} and if so, checks if the type of the value is a collection.
-     */
-    public static boolean valueInstanceOfCollection(final Object o) {
-        return o instanceof GValue && ((GValue) o).getType().isCollection();
-    }
-
-    /**
-     * Tests if the object is a {@link GValue} and if so, checks if the type of the value is a numeric.
-     */
-    public static boolean valueInstanceOfNumeric(final Object o) {
-        return o instanceof GValue && ((GValue) o).getType().isNumeric();
-    }
-
-    /**
-     * Checks the type of the object against the provided {@link GType}. If the object is a {@link GValue} then it
-     * can directly check the type, otherwise it will test the given object's class itself using the mapping on the
-     * {@link GType}.
-     */
-    public static boolean instanceOf(final Object o, final GType type) {
-        if (null == o)
-            return false;
-        else if (o instanceof GValue)
-            return ((GValue) o).getType() == type;
-        else
-            return type.getJavaType().isAssignableFrom(o.getClass());
-    }
-
-    /**
-     * Returns {@code true} if the object is a collection or a {@link GValue} that contains a {@link Collection}.
-     */
-    public static boolean instanceOfCollection(final Object o) {
-        return o instanceof Collection || valueInstanceOfCollection(o);
-    }
-
-    /**
-     * Returns {@code true} if the object is a number or a {@link GValue} that contains a number.
-     */
-    public static boolean instanceOfNumber(final Object o) {
-        return o instanceof Number || valueInstanceOfNumeric(o);
-    }
-
-    public static <T> GValue<T> ensureGValue(final Object o) {
-        return (GValue<T>) of(o);
+    public static boolean valueInstanceOf(final Object o, final Class<?> type) {
+        return o instanceof GValue && !((GValue<?>) o).isNull() && type.isAssignableFrom(((GValue<?>) o).get().getClass());
     }
 
     /**
@@ -377,6 +340,49 @@ public class GValue<V> implements Serializable {
         return values;
     }
 
+    /**
+     * Converts possible {@link GValue} objects argument array to their values to prevent them from leaking to the Graph API.
+     * Providers extending from this step should use this method to get actual values to prevent any {@link GValue}
+     * objects from leaking to the Graph API.
+     */
+    public static Object[] resolveToValues(final Object[] gvalues) {
+        final Object[] values = new Object[gvalues.length];
+        for (int i = 0; i < gvalues.length; i++) {
+            values[i] = gvalues[i] instanceof GValue ? ((GValue<?>) gvalues[i]).get() : gvalues[i];
+            if (values[i] instanceof GValueConstantTraversal) {
+                // TODO:: This seems out of place here, find a better home for this.
+                values[i] = ((GValueConstantTraversal<?, ?>) values[i]).getConstantTraversal();
+            }
+        }
+        return values;
+    }
+
+    /**
+     * Tests if any of the objects are GValues.
+     */
+    public static boolean containsGValues(final Object... args) {
+        for (final Object arg : args) {
+            if (arg instanceof GValue) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Tests if any of the objects are GValues or GValueConstantTraversal, and if so, if they are variables.
+     */
+    public static boolean containsVariables(final Object... args) {
+        for (final Object arg : args) {
+            if (arg instanceof GValue && ((GValue<?>) arg).isVariable()) {
+                return true;
+            } else if (arg instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) arg).isParameterized()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Takes an argument that is either a {@code GValue} or an object and if the former, returns the child object and
      * if the latter returns the object itself.
@@ -387,16 +393,4 @@ public class GValue<V> implements Serializable {
         else
             return either;
     }
-
-    /**
-     * Takes an argument that is either a {@code GValue} or an object and returns its {@code Number} form, otherwise
-     * throws an exception.
-     */
-    public static Number numberOf(final Object either) {
-        final Object o = valueOf(either);
-        if (o instanceof Number)
-            return (Number) o;
-        else
-            throw new IllegalStateException("Argument is not a number");
-    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueHolder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueHolder.java
new file mode 100644
index 0000000000..d2718158e3
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueHolder.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step;
+
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+import java.util.Collection;
+
+public interface GValueHolder<S, E> extends Step<S, E> {
+
+    default void reduce() {
+        // todo: maybe check to see if GValueManager was updated after traversal construction to spare this updateVariable
+        final GValueManager manager = this.getTraversal().getGValueManager();
+        manager.getGValues().forEach(gValue -> updateVariable(gValue.getName(), gValue.get()));
+        Step<S, E> concreteStep = this.asConcreteStep();
+        concreteStep.setId(this.getId());
+        TraversalHelper.replaceStep(this, concreteStep, this.getTraversal());
+    }
+
+    Step<S, E> asConcreteStep();
+
+    boolean isParameterized();
+
+    void updateVariable(String name, Object value);
+
+    Collection<GValue<?>> getGValues();
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java
index 67c3d1e7e4..330ee84f4e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/HasContainerHolder.java
@@ -18,14 +18,21 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step;
 
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
 
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public interface HasContainerHolder {
+public interface HasContainerHolder<S, E> extends GValueHolder<S, E> {
 
     public List<HasContainer> getHasContainers();
 
@@ -34,4 +41,38 @@ public interface HasContainerHolder {
     public default void removeHasContainer(final HasContainer hasContainer) {
         throw new UnsupportedOperationException("The holder does not support container removal: " + this.getClass().getCanonicalName());
     }
+
+    public default Collection<P<?>> getPredicates() {
+        Collection<P<?>> predicates = getHasContainers().stream().map(p -> p.getPredicate()).collect(Collectors.toList());
+        for (P<?> predicate : predicates) {
+            getTraversal().getGValueManager().pinGValues(predicate.getGValues());
+        }
+        return predicates;
+    }
+
+    public default Collection<P<?>> getPredicatesGValueSafe() {
+        return getHasContainers().stream().map(p -> p.getPredicate()).collect(Collectors.toList());
+    }
+
+    public default HasContainerHolder<S, E> asConcreteStep() {
+        return this;
+    }
+
+    public default boolean isParameterized() {
+        return getPredicates().stream().anyMatch(P::isParameterized);
+    }
+
+    public default void updateVariable(final String name, final Object value) {
+        getPredicates().forEach((p) -> {
+            p.updateVariable(name, value);
+        });
+    }
+
+    public default Collection<GValue<?>> getGValues() {
+        Set<GValue<?>> allGValues = new HashSet<>();
+        for (final P<?> p : getPredicates()) {
+            allGValues.addAll(p.getGValues());
+        }
+        return allGValues;
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
index 14553d9e62..07bb350d80 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
@@ -28,7 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  * @author Matt Frantz (http://github.com/mhfrantz)
  */
-public interface Mutating<E extends Event> extends Configuring {
+public interface Mutating<E extends Event> {
 
     /**
      * Gets the callback registry for events that the step raises.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PropertyAdding.java
similarity index 73%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PropertyAdding.java
index db205b6a6c..c37e94cdf7 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PropertyAdding.java
@@ -16,15 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.tinkerpop.gremlin.process.traversal.step;
+package org.apache.tinkerpop.gremlin.process.traversal.step; //TODO:: relocate class
 
-/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public interface Ranging {
-
-    public long getLowRange();
-
-    public long getHighRange();
+import java.util.List;
+import java.util.Map;
 
+public interface PropertyAdding {
+    void addProperty(Object key, Object value);
+    Map<Object, List<Object>> getProperties();
+    boolean removeProperty(Object k);
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
index db205b6a6c..a8568b929d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
@@ -23,8 +23,8 @@ package org.apache.tinkerpop.gremlin.process.traversal.step;
  */
 public interface Ranging {
 
-    public long getLowRange();
+    public Long getLowRange();
 
-    public long getHighRange();
+    public Long getHighRange();
 
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
index 3a43f7b74c..e9e6195b51 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step;
 
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -84,6 +85,10 @@ public interface TraversalParent extends PopContaining, AutoCloseable {
         childTraversal.setParent(this);
         childTraversal.getSideEffects().mergeInto(this.asStep().getTraversal().getSideEffects());
         childTraversal.setSideEffects(this.asStep().getTraversal().getSideEffects());
+        final GValueManager childManager = childTraversal.getGValueManager();
+        final GValueManager parentManager = this.asStep().getTraversal().getGValueManager();
+        childManager.mergeInto(parentManager);
+        TraversalHelper.applyTraversalRecursively(t -> t.setGValueManager(this.asStep().getTraversal().getGValueManager()), childTraversal);
         return (Traversal.Admin<S, E>) childTraversal;
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
index ddefbb20a5..b0a21471cd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
@@ -21,7 +21,6 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Deleting;
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
@@ -62,17 +61,4 @@ public class DropStep<S> extends FilterStep<S> implements Deleting<Event> {
         if (null == callbackRegistry) callbackRegistry = new ListCallbackRegistry<>();
         return callbackRegistry;
     }
-
-    /**
-     * This method doesn't do anything as {@code drop()} doesn't take property mutation arguments.
-     */
-    @Override
-    public void configure(final Object... keyValues) {
-        // do nothing
-    }
-
-    @Override
-    public Parameters getParameters() {
-        return Parameters.EMPTY;
-    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java
index 6048ba78fe..42c24f41a8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java
@@ -38,7 +38,7 @@ import java.util.Set;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public class HasStep<S extends Element> extends FilterStep<S> implements HasContainerHolder, Configuring {
+public class HasStep<S extends Element> extends FilterStep<S> implements HasContainerHolder<S, S>, Configuring {
 
     private final Parameters parameters = new Parameters();
     private List<HasContainer> hasContainers;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java
index a2889864a8..256900f0a8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java
@@ -31,7 +31,7 @@ import java.util.Set;
  * @author Daniel Kuppitz (http://gremlin.guru)
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class IsStep<S> extends FilterStep<S> {
+public final class IsStep<S> extends FilterStep<S> implements IsStepContract<S> {
 
     private P<S> predicate;
 
@@ -50,7 +50,7 @@ public final class IsStep<S> extends FilterStep<S> {
         return StringFactory.stepString(this, this.predicate);
     }
 
-
+    @Override
     public P<S> getPredicate() {
         return this.predicate;
     }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepTest.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepContract.java
similarity index 62%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepTest.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepContract.java
index d59ae5cbb0..e83cbbf7fc 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepTest.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepContract.java
@@ -19,24 +19,22 @@
 package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 
 import org.apache.tinkerpop.gremlin.process.traversal.P;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 
-import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
 
 /**
- * @author Daniel Kuppitz (http://gremlin.guru)
+ * Defines the contract for steps containing a single {@link P}.
  */
-public class IsStepTest extends StepTest {
+public interface IsStepContract<S> extends Step<S, S> {
 
-    @Override
-    protected List<Traversal> getTraversals() {
-        return Arrays.asList(
-                __.count().is(0),
-                __.count().is(P.gt(0)),
-                __.count().is(1)
-        );
-    }
+    /**
+     * Retrieves the step's predicate.
+     *
+     * @return the predicate associated with the step
+     */
+    public P<?> getPredicate();
+
+    public Set<TraverserRequirement> getRequirements();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepPlaceholder.java
similarity index 51%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepPlaceholder.java
index a2889864a8..e2897d5fec 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStepPlaceholder.java
@@ -21,43 +21,47 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
-import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 
+import java.util.Collection;
 import java.util.EnumSet;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
-/**
- * @author Daniel Kuppitz (http://gremlin.guru)
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public final class IsStep<S> extends FilterStep<S> {
+public final class IsStepPlaceholder<S> extends AbstractStep<S,S> implements GValueHolder<S, S>, IsStepContract<S> {
 
     private P<S> predicate;
 
-    public IsStep(final Traversal.Admin traversal, final P<S> predicate) {
+    public IsStepPlaceholder(final Traversal.Admin traversal, final P<S> predicate) {
         super(traversal);
         this.predicate = predicate;
+        traversal.getGValueManager().register(predicate.getGValues());
     }
 
     @Override
-    protected boolean filter(final Traverser.Admin<S> traverser) {
-        return this.predicate.test(traverser.get());
+    protected Traverser.Admin<S> processNextStart() throws NoSuchElementException {
+        throw new IllegalStateException("IsStepPlaceholder is not executable");
     }
 
     @Override
-    public String toString() {
-        return StringFactory.stepString(this, this.predicate);
+    public P<S> getPredicate() {
+        if (predicate.isParameterized()) {
+            traversal.getGValueManager().pinGValues(predicate.getGValues());
+        }
+        return this.predicate;
     }
 
-
-    public P<S> getPredicate() {
+    public P<S> getPredicateGValueSafe() {
         return this.predicate;
     }
 
     @Override
-    public IsStep<S> clone() {
-        final IsStep<S> clone = (IsStep<S>) super.clone();
+    public IsStepPlaceholder<S> clone() {
+        final IsStepPlaceholder<S> clone = (IsStepPlaceholder<S>) super.clone();
         clone.predicate = this.predicate.clone();
         return clone;
     }
@@ -72,4 +76,25 @@ public final class IsStep<S> extends FilterStep<S> {
         return EnumSet.of(TraverserRequirement.OBJECT);
     }
 
+    @Override
+    public IsStep<S> asConcreteStep() {
+        IsStep<S> step = new IsStep<>(traversal, predicate);
+        TraversalHelper.copyLabels(this, step, false);
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return predicate.isParameterized();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        predicate.updateVariable(name, (S) value);
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        return predicate.getGValues();
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
index da1c7769e6..792ab2d645 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
@@ -22,7 +22,6 @@ import org.apache.tinkerpop.gremlin.process.computer.MemoryComputeKey;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Bypassing;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.step.FilteringBarrier;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Ranging;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -42,20 +41,16 @@ import java.util.function.BinaryOperator;
  * @author Bob Briody (http://bobbriody.com)
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging, Bypassing, FilteringBarrier<TraverserSet<S>> {
+public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging, Bypassing, FilteringBarrier<TraverserSet<S>>, RangeGlobalStepContract<S> {
 
-    private GValue<Long> low;
-    private final GValue<Long> high;
+    private long low;
+    private long high;
     private AtomicLong counter = new AtomicLong(0l);
     private boolean bypass;
 
     public RangeGlobalStep(final Traversal.Admin traversal, final long low, final long high) {
-        this(traversal, GValue.ofLong(null, low), GValue.ofLong(null, high));
-    }
-
-    public RangeGlobalStep(final Traversal.Admin traversal, final GValue<Long> low, final GValue<Long> high) {
         super(traversal);
-        if (low.get() != -1 && high.get() != -1 && low.get() > high.get()) {
+        if (low != -1 && high != -1 && low > high) {
             throw new IllegalArgumentException("Not a legal range: [" + low + ", " + high + ']');
         }
         this.low = low;
@@ -66,12 +61,12 @@ public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging,
     protected boolean filter(final Traverser.Admin<S> traverser) {
         if (this.bypass) return true;
 
-        if (this.high.get() != -1 && this.counter.get() >= this.high.get()) {
+        if (this.high != -1 && this.counter.get() >= this.high) {
             throw FastNoSuchElementException.instance();
         }
 
         long avail = traverser.bulk();
-        if (this.counter.get() + avail <= this.low.get()) {
+        if (this.counter.get() + avail <= this.low) {
             // Will not surpass the low w/ this traverser. Skip and filter the whole thing.
             this.counter.getAndAdd(avail);
             return false;
@@ -80,13 +75,13 @@ public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging,
         // Skip for the low and trim for the high. Both can happen at once.
 
         long toSkip = 0;
-        if (this.counter.get() < this.low.get()) {
-            toSkip = this.low.get() - this.counter.get();
+        if (this.counter.get() < this.low) {
+            toSkip = this.low - this.counter.get();
         }
 
         long toTrim = 0;
-        if (this.high.get() != -1 && this.counter.get() + avail >= this.high.get()) {
-            toTrim = this.counter.get() + avail - this.high.get();
+        if (this.high != -1 && this.counter.get() + avail >= this.high) {
+            toTrim = this.counter.get() + avail - this.high;
         }
 
         long toEmit = avail - toSkip - toTrim;
@@ -104,17 +99,17 @@ public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging,
 
     @Override
     public String toString() {
-        return StringFactory.stepString(this, this.low.get(), this.high.get());
+        return StringFactory.stepString(this, this.low, this.high);
     }
 
     @Override
-    public long getLowRange() {
-        return this.low.get();
+    public Long getLowRange() {
+        return this.low;
     }
 
     @Override
-    public long getHighRange() {
-        return this.high.get();
+    public Long getHighRange() {
+        return this.high;
     }
 
     @Override
@@ -126,14 +121,14 @@ public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging,
 
     @Override
     public int hashCode() {
-        return super.hashCode() ^ Long.hashCode(this.low.get()) ^ Long.hashCode(this.high.get());
+        return super.hashCode() ^ Long.hashCode(this.low) ^ Long.hashCode(this.high);
     }
 
     @Override
     public boolean equals(final Object other) {
         if (super.equals(other)) {
             final RangeGlobalStep typedOther = (RangeGlobalStep) other;
-            return typedOther.low.get() == this.low.get() && typedOther.high.get() == this.high.get();
+            return typedOther.low == this.low && typedOther.high == this.high;
         }
         return false;
     }
@@ -146,7 +141,7 @@ public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging,
 
     @Override
     public MemoryComputeKey<TraverserSet<S>> getMemoryComputeKey() {
-        return MemoryComputeKey.of(this.getId(), new RangeBiOperator<>(this.high.get()), false, true);
+        return MemoryComputeKey.of(this.getId(), new RangeBiOperator<>(this.high), false, true);
     }
 
     @Override
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepTest.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStepContract.java
similarity index 63%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepTest.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStepContract.java
index 856df87cb2..c2a6f46e9e 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepTest.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStepContract.java
@@ -18,23 +18,24 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
-
-import java.util.Arrays;
-import java.util.List;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
 
 /**
- * @author Daniel Kuppitz (http://gremlin.guru)
+ * Defines the contract for {@code range} related steps.
  */
-public class TailGlobalStepTest extends StepTest {
+public interface RangeGlobalStepContract<S> extends Step<S, S> {
+
+    /**
+     * Retrieves the lower bound of the range.
+     *
+     * @return the value representing the lower bound of the range
+     */
+    Long getLowRange();
 
-    @Override
-    protected List<Traversal> getTraversals() {
-        return Arrays.asList(
-                __.tail(5L),
-                __.tail(10L)
-        );
-    }
+    /**
+     * Retrieves the higher bound of the range.
+     *
+     * @return the higher bound of the range as an object of type V
+     */
+    Long getHighRange();
 }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepTest.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeLocalStepContract.java
similarity index 63%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepTest.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeLocalStepContract.java
index 856df87cb2..a57b6fed3f 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepTest.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeLocalStepContract.java
@@ -18,23 +18,24 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
-
-import java.util.Arrays;
-import java.util.List;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
 
 /**
- * @author Daniel Kuppitz (http://gremlin.guru)
+ * Defines the contract for {@code range} related steps.
  */
-public class TailGlobalStepTest extends StepTest {
+public interface RangeLocalStepContract<S> extends Step<S, S> {
+
+    /**
+     * Retrieves the lower bound of the range.
+     *
+     * @return the value representing the lower bound of the range
+     */
+    Long getLowRange();
 
-    @Override
-    protected List<Traversal> getTraversals() {
-        return Arrays.asList(
-                __.tail(5L),
-                __.tail(10L)
-        );
-    }
+    /**
+     * Retrieves the higher bound of the range.
+     *
+     * @return the higher bound of the range as an object of type V
+     */
+    Long getHighRange();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeStepPlaceholder.java
new file mode 100644
index 0000000000..145b8229d4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeStepPlaceholder.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.RangeLocalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+public abstract class RangeStepPlaceholder<S> extends AbstractStep<S,S> implements GValueHolder<S, S> {
+
+    protected GValue<Long> low;
+    protected GValue<Long> high;
+
+    public RangeStepPlaceholder(final Traversal.Admin traversal, final GValue<Long> low, final GValue<Long> high) {
+        super(traversal);
+        this.low = low;
+        this.high = high;
+        traversal.getGValueManager().register(low);
+        traversal.getGValueManager().register(high);
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return low.isVariable() || high.isVariable();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (name.equals(low.getName())) {
+            if (!(value instanceof Number)) {
+                throw new IllegalArgumentException("The variable " + name + " must have a value of type Number");
+            }
+            this.low = GValue.ofLong(name, ((Number) value).longValue());
+        }
+
+        if (name.equals(high.getName())) {
+            if (!(value instanceof Number)) {
+                throw new IllegalArgumentException("The variable " + name + " must have a value of type Number");
+            }
+            this.high = GValue.ofLong(name, ((Number) value).longValue());
+        }
+    }
+
+    public Long getLowRange() {
+        if (low == null) {
+            return null;
+        }
+        if (low.isVariable()) {
+            this.traversal.getGValueManager().pinVariable(low.getName());
+        }
+        return low.get();
+    }
+
+    public Long getHighRange() {
+        if (high == null) {
+            return null;
+        }
+        if (high.isVariable()) {
+            this.traversal.getGValueManager().pinVariable(high.getName());
+        }
+        return high.get();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        RangeStepPlaceholder<?> that = (RangeStepPlaceholder<?>) o;
+        return Objects.equals(low, that.low) && Objects.equals(high, that.high);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), low, high);
+    }
+
+    /**
+     * getLowRange without marking the GValue as dirty and thus automatically pinning the GValue. It is the caller's
+     * responsibility to ensure that this value is not used to alter the traversal in any way which is not generalizable
+     * to any parameter value.
+     * @return the lower bound for range().
+     */
+    public long getLowRangeGValueSafe() {
+        return low.get();
+    }
+
+    /**
+     * getHighRange without marking the GValue as dirty and thus automatically pinning the GValue. It is the caller's
+     * responsibility to ensure that this value is not used to alter the traversal in any way which is not generalizable
+     * to any parameter value.
+     * @return the upper bound for range().
+     */
+    public long getHighRangeGValueSafe() {
+        return high.get();
+    }
+
+    public String getLowName() {
+        return low.getName();
+    }
+
+    public String getHighName() {
+        return high.getName();
+    }
+
+    @Override
+    protected Traverser.Admin<S> processNextStart() throws NoSuchElementException {
+        throw new IllegalStateException("RangeGlobalGValueContract is not executable");
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Set<GValue<?>> gValues = new HashSet<>();
+        if (low.isVariable()) {
+            gValues.add(low);
+        }
+        if (high.isVariable()) {
+            gValues.add(high);
+        }
+        return gValues;
+    }
+
+    public static class RangeGlobalStepPlaceholder<S> extends RangeStepPlaceholder<S> implements RangeGlobalStepContract<S> {
+
+        public RangeGlobalStepPlaceholder(final Traversal.Admin traversal, final GValue<Long> low, final GValue<Long> high) {
+            super(traversal, low, high);
+        }
+
+        @Override
+        public RangeGlobalStep<S> asConcreteStep() {
+            RangeGlobalStep<S> step = new RangeGlobalStep<>(traversal, low.get(), high.get());
+            TraversalHelper.copyLabels(this, step, false);
+            return step;
+        }
+
+        @Override
+        public RangeGlobalStepPlaceholder<S> clone() {
+            return new RangeGlobalStepPlaceholder<>(traversal, low, high);
+        }
+    }
+
+    public static class RangeLocalStepPlaceholder<S> extends RangeStepPlaceholder<S> implements RangeLocalStepContract<S> {
+
+        public RangeLocalStepPlaceholder(final Traversal.Admin traversal, final GValue<Long> low, final GValue<Long> high) {
+            super(traversal, low, high);
+        }
+
+        @Override
+        public RangeLocalStep<S> asConcreteStep() {
+            RangeLocalStep<S> step = new RangeLocalStep<>(traversal, low.get(), high.get());
+            TraversalHelper.copyLabels(this, step, false);
+            return step;
+        }
+
+        @Override
+        public RangeLocalStepPlaceholder<S> clone() {
+            return new RangeLocalStepPlaceholder<>(traversal, low, high);
+        }
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStep.java
index fde75ea3ad..6a443e2de6 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStep.java
@@ -22,7 +22,6 @@ import org.apache.tinkerpop.gremlin.process.computer.MemoryComputeKey;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Bypassing;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.step.FilteringBarrier;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -40,23 +39,20 @@ import java.util.Set;
 /**
  * @author Matt Frantz (http://github.com/mhfrantz)
  */
-public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypassing, FilteringBarrier<TraverserSet<S>> {
+public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypassing, FilteringBarrier<TraverserSet<S>>, TailGlobalStepContract<S> {
 
-    private final GValue<Long> limit;
+    private final long limit;
     private Deque<Traverser.Admin<S>> tail;
     private long tailBulk = 0L;
     private boolean bypass = false;
 
     public TailGlobalStep(final Traversal.Admin traversal, final long limit) {
-        this(traversal, GValue.ofLong(null, limit));
-    }
-
-    public TailGlobalStep(final Traversal.Admin traversal, final GValue<Long> limit) {
         super(traversal);
         this.limit = limit;
-        this.tail = new ArrayDeque<>(limit.get().intValue());
+        this.tail = new ArrayDeque<>((int) limit);
     }
 
+    @Override
     public void setBypass(final boolean bypass) {
         this.bypass = bypass;
     }
@@ -74,7 +70,7 @@ public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypas
             // Pull the oldest traverser from the tail buffer.
             final Traverser.Admin<S> oldest = this.tail.pop();
             // Trim any excess from the oldest traverser.
-            final long excess = this.tailBulk - this.limit.get();
+            final long excess = this.tailBulk - this.limit;
             if (excess > 0) {
                 oldest.setBulk(oldest.bulk() - excess);
                 // Account for the loss of excess in the tail buffer
@@ -95,20 +91,20 @@ public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypas
 
     @Override
     public String toString() {
-        return StringFactory.stepString(this, this.limit.get());
+        return StringFactory.stepString(this, this.limit);
     }
 
     @Override
     public TailGlobalStep<S> clone() {
         final TailGlobalStep<S> clone = (TailGlobalStep<S>) super.clone();
-        clone.tail = new ArrayDeque<>(this.limit.get().intValue());
+        clone.tail = new ArrayDeque<>((int) this.limit);
         clone.tailBulk = 0L;
         return clone;
     }
 
     @Override
     public int hashCode() {
-        return super.hashCode() ^ Long.hashCode(this.limit.get());
+        return super.hashCode() ^ Long.hashCode(this.limit);
     }
 
     @Override
@@ -123,7 +119,7 @@ public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypas
         while (!this.tail.isEmpty()) {
             final Traverser.Admin<S> oldest = this.tail.getFirst();
             final long bulk = oldest.bulk();
-            if (this.tailBulk - bulk < limit.get())
+            if (this.tailBulk - bulk < limit)
                 break;
             this.tail.pop();
             this.tailBulk -= bulk;
@@ -134,7 +130,7 @@ public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypas
 
     @Override
     public MemoryComputeKey<TraverserSet<S>> getMemoryComputeKey() {
-        return MemoryComputeKey.of(this.getId(), new RangeGlobalStep.RangeBiOperator<>(this.limit.get()), false, true);
+        return MemoryComputeKey.of(this.getId(), new RangeGlobalStep.RangeBiOperator<>(this.limit), false, true);
     }
 
     @Override
@@ -170,4 +166,9 @@ public final class TailGlobalStep<S> extends AbstractStep<S, S> implements Bypas
             this.addStart(traverser);
         });
     }
+
+    @Override
+    public Long getLimit() {
+        return limit;
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepContract.java
similarity index 78%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepContract.java
index db205b6a6c..9c113880ef 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepContract.java
@@ -16,15 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.tinkerpop.gremlin.process.traversal.step;
+package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 
-/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public interface Ranging {
-
-    public long getLowRange();
-
-    public long getHighRange();
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
 
+public interface TailGlobalStepContract<S> extends Step<S, S> {
+    Long getLimit();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepPlaceholder.java
new file mode 100644
index 0000000000..4b554056f5
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailGlobalStepPlaceholder.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * @author Matt Frantz (http://github.com/mhfrantz)
+ */
+public final class TailGlobalStepPlaceholder<S> extends AbstractStep<S, S> implements TailGlobalStepContract<S>, GValueHolder<S, S> {
+
+    private GValue<Long> limit;
+
+    public TailGlobalStepPlaceholder(final Traversal.Admin traversal, final GValue<Long> limit) {
+        super(traversal);
+        this.limit = limit;
+        if (this.limit.isVariable()) {
+            traversal.getGValueManager().register(limit);
+        }
+    }
+
+    @Override
+    public Traverser.Admin<S> processNextStart() {
+        throw new IllegalStateException("GValueHolder is not executable");
+    }
+
+    @Override
+    public TailGlobalStepPlaceholder<S> clone() {
+        final TailGlobalStepPlaceholder<S> clone = (TailGlobalStepPlaceholder<S>) super.clone();
+        return clone;
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ^ Objects.hashCode(this.limit);
+    }
+
+    @Override
+    public Long getLimit() {
+        if (limit.isVariable()) {
+            traversal.getGValueManager().pinVariable(limit.getName());
+        }
+        return limit.get();
+    }
+
+    public Long getLimitGValueSafe() {
+        return limit.get();
+    }
+
+    @Override
+    public Step<S, S> asConcreteStep() {
+        TailGlobalStep<S> step = new TailGlobalStep<>(traversal, limit.get());
+        TraversalHelper.copyLabels(this, step, false);
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return limit.isVariable();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (name.equals(limit.getName())) {
+            if (!(value instanceof Number)) {
+                throw new IllegalArgumentException("The variable " + name + " must have a value of type Number");
+            }
+            this.limit = GValue.ofLong(name, ((Number) value).longValue());
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        return Collections.singletonList(limit);
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailLocalStepContract.java
similarity index 78%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailLocalStepContract.java
index db205b6a6c..f72640eec4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailLocalStepContract.java
@@ -16,15 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.tinkerpop.gremlin.process.traversal.step;
+package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 
-/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public interface Ranging {
-
-    public long getLowRange();
-
-    public long getHighRange();
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
 
+public interface TailLocalStepContract<S> extends Step<S, S> {
+    Long getLimit();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailLocalStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailLocalStepPlaceholder.java
new file mode 100644
index 0000000000..e36edd6e33
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TailLocalStepPlaceholder.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.TailLocalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+public final class TailLocalStepPlaceholder<S> extends AbstractStep<S, S> implements TailLocalStepContract<S>, GValueHolder<S, S> {
+
+    private GValue<Long> limit;
+
+    public TailLocalStepPlaceholder(final Traversal.Admin traversal, final GValue<Long> limit) {
+        super(traversal);
+        this.limit = limit;
+        if (this.limit.isVariable()) {
+            traversal.getGValueManager().register(limit);
+        }
+    }
+
+    @Override
+    public Traverser.Admin<S> processNextStart() {
+        throw new IllegalStateException("GValueHolder is not executable");
+    }
+
+    @Override
+    public TailLocalStepPlaceholder<S> clone() {
+        final TailLocalStepPlaceholder<S> clone = (TailLocalStepPlaceholder<S>) super.clone();
+        return clone;
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ^ Objects.hashCode(this.limit);
+    }
+
+    @Override
+    public Long getLimit() {
+        if (limit.isVariable()) {
+            traversal.getGValueManager().pinVariable(limit.getName());
+        }
+        return limit.get();
+    }
+
+    public Long getLimitGValueSafe() {
+        return limit.get();
+    }
+
+    @Override
+    public TailLocalStep<S> asConcreteStep() {
+        TailLocalStep<S> step = new TailLocalStep<>(traversal, limit.get());
+        TraversalHelper.copyLabels(this, step, false);
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return limit.isVariable();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (name.equals(limit.getName())) {
+            if (!(value instanceof Number)) {
+                throw new IllegalArgumentException("The variable " + name + " must have a value of type Number");
+            }
+            this.limit = GValue.ofLong(name, ((Number) value).longValue());
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        return Collections.singletonList(limit);
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AbstractAddElementStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AbstractAddElementStepPlaceholder.java
new file mode 100644
index 0000000000..7783711d5b
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AbstractAddElementStepPlaceholder.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueHelper;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+public abstract class AbstractAddElementStepPlaceholder<S, E extends Element, X extends Event> extends AbstractStep<S, E>
+        implements AddElementStepContract<S, E>, GValueHolder<S, E>, Writing<X> {
+
+    protected Traversal.Admin<S, String> label;
+    protected Map<Object, List<Object>> properties = new HashMap<>();
+    protected GValue<Object> elementId;
+    protected Set<String> scopeKeys = new HashSet<>();
+
+    public AbstractAddElementStepPlaceholder(final Traversal.Admin traversal, final String label) {
+        this(traversal, label == null ? null : new ConstantTraversal<>(label));
+    }
+
+    public AbstractAddElementStepPlaceholder(final Traversal.Admin traversal, final GValue<String> label) {
+        this(traversal.asAdmin(), label == null ? null : new GValueConstantTraversal<>(label));
+    }
+
+    public AbstractAddElementStepPlaceholder(final Traversal.Admin traversal, final Traversal.Admin<S,String> labelTraversal) {
+        super(traversal);
+        if (labelTraversal == null) {
+            throw new IllegalArgumentException("The label provided must not be null");
+        }
+        this.label = labelTraversal;
+        if (labelTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<S, String>) labelTraversal).getGValue());
+        }
+        addTraversal(labelTraversal);
+    }
+
+    @Override
+    public Set<String> getScopeKeys() {
+        return Collections.unmodifiableSet(scopeKeys);
+    }
+
+    protected void addTraversal(final Traversal.Admin<?, ?> traversal) {
+        TraversalHelper.getStepsOfAssignableClassRecursively(Scoping.class, traversal).forEach(s -> scopeKeys.addAll(s.getScopeKeys()));
+    }
+
+    @Override
+    public List<Traversal.Admin<S, E>> getLocalChildren() {
+        List<Traversal.Admin<S, E>> childTraversals = new ArrayList<>();
+        for (List<Object> values : properties.values()) {
+            for (Object value : values) {
+                if (value instanceof Traversal) {
+                    childTraversals.add((Traversal.Admin<S, E>) ((Traversal) value).asAdmin());
+                }
+            }
+        }
+        return childTraversals;
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT);
+    }
+
+    @Override
+    protected Traverser.Admin<E> processNextStart() throws NoSuchElementException {
+        throw new IllegalStateException("GValuePlaceholder step is not executable");
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        if (label != null) {
+            hash ^= label.hashCode();
+        }
+        if (properties != null) {
+            for (Map.Entry<Object, List<Object>> entry : properties.entrySet()) {
+                hash ^= Objects.hashCode(entry.getKey());
+                hash ^= Objects.hashCode(entry.getValue());
+            };
+        }
+        return hash;
+    }
+
+    protected void configureConcreteStep(AddElementStepContract<S, E> step) {
+        for (final Map.Entry<Object, List<Object>> entry : properties.entrySet()) {
+            for (Object value : entry.getValue()) {
+                step.addProperty(entry.getKey(), value instanceof GValue ? ((GValue<?>) value).get() : value);
+            }
+        }
+        if (elementId != null) {
+            step.setElementId(elementId.get());
+        }
+        TraversalHelper.copyLabels(this, step, false);
+    }
+
+    @Override
+    public boolean isParameterized() {
+        if (label instanceof GValueConstantTraversal && ((GValueConstantTraversal<S, String>) label).isParameterized() ||
+                (elementId != null && elementId.isVariable())) {
+            return true;
+        }
+        for (List<Object> list : properties.values()) {
+            if (GValue.containsVariables(list.toArray())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getLabel() {
+        if (label instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, ?>) label).getGValue().getName());
+        }
+        return label.next();
+    }
+
+    public String getLabelGValueSafe() {
+        return label.next();
+    }
+
+    private void setLabel(Object label) {// TODO should this be public and added to step interface?
+        if (getLabelGValueSafe().equals(Vertex.DEFAULT_LABEL)) {
+            this.label = label instanceof GValue ? new GValueConstantTraversal<>((GValue) label) : new ConstantTraversal<>((String) label);
+            if (label instanceof GValue) {
+                traversal.getGValueManager().register((GValue<?>) label);
+            }
+        }
+    }
+
+    @Override
+    public void addProperty(Object key, Object value) {
+        if (key instanceof GValue) {
+            throw new IllegalArgumentException("GValue cannot be used as a property key");
+        }
+        if (value instanceof GValue) {
+            traversal.getGValueManager().register((GValue<?>) value);
+        }
+        if (key == T.label) {
+            setLabel(value);
+            return;
+        }
+        if (key == T.id) {
+            setElementId(value);
+            return;
+        }
+        if (!supportsMultiProperties() && properties.containsKey(key)) {
+            throw new IllegalArgumentException("Multi-properties are not supported by this step");
+        }
+        if (value instanceof Traversal) {
+            this.integrateChild(((Traversal<?,?>) value).asAdmin());
+        }
+        List<Object> values = properties.get(key);
+        if (values == null) {
+            values = new ArrayList<>();
+            properties.put(key, values);
+        }
+        values.add(value);
+    }
+
+    protected abstract boolean supportsMultiProperties();
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return GValueHelper.resolveProperties(properties,
+                gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Map<Object, List<Object>> getPropertiesGValueSafe() {
+        return GValueHelper.resolveProperties(properties);
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (properties.containsKey(k)) {
+            properties.remove(k);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object getElementId() {
+        if (elementId == null) {
+            return null;
+        }
+        if (elementId.isVariable()) {
+            this.traversal.getGValueManager().pinVariable(elementId.getName());
+        }
+        return elementId.get();
+    }
+
+    public Object getElementIdGValueSafe() {
+        if (elementId == null) {
+            return null;
+        }
+        return elementId.get();
+    }
+
+    @Override
+    public boolean removeElementId() {
+        if (elementId == null) {
+            return false;
+        }
+        elementId = null;
+        return true;
+    }
+
+    @Override
+    public void setElementId(Object elementId) {
+        this.elementId = elementId instanceof GValue ? (GValue<Object>) elementId : GValue.of(elementId);
+        this.traversal.getGValueManager().register(this.elementId);
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (label instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<S, String>) label).updateVariable(name, value);
+        }
+        if (elementId != null && name.equals(elementId.getName())) {
+            elementId = GValue.of(name, value);
+        }
+        for (final Map.Entry<Object, List<Object>> entry : properties.entrySet()) {
+            for (final Object propertyVal : entry.getValue()) {
+                if (propertyVal instanceof GValue && name.equals(((GValue<?>) propertyVal).getName())) {
+                    properties.put(entry.getKey(), Collections.singletonList(GValue.of(name, value)));
+                }
+            }
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Set<GValue<?>> gValues = GValueHelper.getGValuesFromProperties(properties);
+        if (label instanceof GValueConstantTraversal && ((GValueConstantTraversal<S, String>) label).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<S, String>) label).getGValue());
+        }
+        if (elementId != null && elementId.isVariable()) {
+            gValues.add(elementId);
+        }
+        return gValues;
+    }
+
+    @Override
+    public CallbackRegistry<X> getMutatingCallbackRegistry() {
+        throw new IllegalStateException("Cannot get mutating CallbackRegistry on GValue placeholder step");
+    }
+
+    @Override
+    public AbstractAddElementStepPlaceholder<S, E, X> clone() {
+        final AbstractAddElementStepPlaceholder<S, E, X> clone = (AbstractAddElementStepPlaceholder) super.clone();
+        if (label != null) {
+            clone.label = label.clone();
+        }
+        clone.properties.putAll(properties);
+        clone.elementId = elementId;
+        clone.scopeKeys.addAll(scopeKeys);
+        return clone;
+    }
+
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
index 5677a70f73..2fe2827659 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
@@ -19,15 +19,16 @@
 
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
-import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
@@ -44,15 +45,11 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
 
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
 public class AddEdgeStartStep extends AbstractStep<Edge, Edge>
-        implements Writing<Event.EdgeAddedEvent>, TraversalParent, Scoping, FromToModulating {
+        implements AddEdgeStepContract<Edge>, Configuring {
 
     private static final String FROM = Graph.Hidden.hide("from");
     private static final String TO = Graph.Hidden.hide("to");
@@ -71,11 +68,6 @@ public class AddEdgeStartStep extends AbstractStep<Edge, Edge>
         this.parameters.set(this, T.label, edgeLabelTraversal);
     }
 
-    public AddEdgeStartStep(final Traversal.Admin traversal, final GValue<String> edgeLabel) {
-        super(traversal);
-        this.parameters.set(this, T.label, edgeLabel.get());
-    }
-
     @Override
     public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
         return this.parameters.getTraversals();
@@ -91,13 +83,6 @@ public class AddEdgeStartStep extends AbstractStep<Edge, Edge>
         return this.parameters.getReferencedLabels();
     }
 
-    @Override
-    public HashSet<PopInstruction> getPopInstructions() {
-        final HashSet<PopInstruction> popInstructions = new HashSet<>();
-        popInstructions.addAll(TraversalParent.super.getPopInstructions());
-        return popInstructions;
-    }
-
     @Override
     public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
@@ -113,6 +98,17 @@ public class AddEdgeStartStep extends AbstractStep<Edge, Edge>
         this.parameters.set(this, FROM, fromObject);
     }
 
+    @Override
+    public Object getElementId() {
+        List<Object> ids = this.parameters.get(T.id, null);
+        return ids.isEmpty() ? null : ids.get(0);
+    }
+
+    @Override
+    public void setElementId(Object elementId) {
+        configure(T.id, elementId);
+    }
+
     @Override
     protected Traverser.Admin<Edge> processNextStart() {
         if (this.first) {
@@ -203,4 +199,50 @@ public class AddEdgeStartStep extends AbstractStep<Edge, Edge>
         return clone;
     }
 
+    @Override
+    public Object getLabel() {
+        Object label = parameters.get(T.label, () -> Edge.DEFAULT_LABEL).get(0);
+        if (label instanceof ConstantTraversal) {
+            return ((ConstantTraversal<?, ?>) label).next();
+        }
+        return label;
+    }
+
+    @Override
+    public Object getFrom() {
+        return getAdjacentVertex(this.parameters, FROM);
+    }
+
+    @Override
+    public Object getTo() {
+        return getAdjacentVertex(this.parameters, TO);
+    }
+    
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return Collections.unmodifiableMap(parameters.getRaw());
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (parameters.contains(k)) {
+            parameters.remove(k);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeElementId() {
+        if (this.parameters.contains(T.id)) {
+            this.parameters.remove(T.id);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void addProperty(final Object key, final Object value) {
+        configure(key, value);
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStepPlaceholder.java
new file mode 100644
index 0000000000..e742af4286
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStepPlaceholder.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PropertyAdding;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+
+import java.util.Collection;
+
+public class AddEdgeStartStepPlaceholder extends AbstractAddElementStepPlaceholder<Edge, Edge, Event.EdgeAddedEvent>
+        implements AddEdgeStepContract<Edge>, GValueHolder<Edge, Edge>, PropertyAdding {
+
+    private Traversal.Admin<?,?> from;
+    private Traversal.Admin<?,?> to;
+
+    public AddEdgeStartStepPlaceholder(final Traversal.Admin traversal, final String edgeLabel) {
+        super(traversal, edgeLabel == null ? Edge.DEFAULT_LABEL : edgeLabel);
+    }
+
+    public AddEdgeStartStepPlaceholder(final Traversal.Admin traversal, final GValue<String> edgeLabel) {
+        super(traversal, edgeLabel == null || edgeLabel.isNull() ? GValue.of(Edge.DEFAULT_LABEL) : edgeLabel);
+    }
+
+    public AddEdgeStartStepPlaceholder(final Traversal.Admin traversal, final Traversal.Admin<?,String> edgeLabelTraversal) {
+        super(traversal, edgeLabelTraversal == null ?
+                new ConstantTraversal<>(Edge.DEFAULT_LABEL) :
+                (Traversal.Admin<Edge, String>) edgeLabelTraversal);
+    }
+
+    @Override
+    public void addTo(final Traversal.Admin<?, ?> toObject) {
+        addTraversal(toObject);
+        if (toObject instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, ?>) toObject).getGValue());
+        }
+        this.to = toObject;
+    }
+
+    @Override
+    public void addFrom(final Traversal.Admin<?, ?> fromObject) {
+        addTraversal(fromObject);
+        if (fromObject instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, ?>) fromObject).getGValue());
+        }
+        this.from = fromObject;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        if (from != null) {
+            hash ^= from.hashCode();
+        }
+        if (to != null) {
+            hash ^= to.hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public AddEdgeStartStep asConcreteStep() {
+        AddEdgeStartStep step = new AddEdgeStartStep(traversal, label instanceof GValueConstantTraversal ? ((GValueConstantTraversal<?, String>) label).getConstantTraversal() : label);
+        super.configureConcreteStep(step);
+        if (from != null) {
+            step.addFrom(from instanceof GValueConstantTraversal ? ((GValueConstantTraversal<?, String>) from).getConstantTraversal() : from);
+        }
+        if (to != null) {
+            step.addTo(to instanceof GValueConstantTraversal ? ((GValueConstantTraversal<?, String>) to).getConstantTraversal() : to);
+        }
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        if (super.isParameterized() ||
+                (from instanceof GValueConstantTraversal && ((GValueConstantTraversal<Edge, String>) from).isParameterized()) ||
+                (to instanceof GValueConstantTraversal && ((GValueConstantTraversal<Edge, String>) to).isParameterized())) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean supportsMultiProperties() {
+        return false;
+    }
+
+    @Override
+    public Object getFrom() {
+        return resolveVertexTraversal(from, gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Object getFromGValueSafe() {
+        return resolveVertexTraversal(from);
+    }
+
+    @Override
+    public Object getTo() {
+        return resolveVertexTraversal(to, gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Object getToGValueSafe() {
+        return resolveVertexTraversal(to);
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        super.updateVariable(name, value);
+        if (from instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<Edge, String>) from).updateVariable(name, value);
+        }
+        if (to instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<Edge, String>) to).updateVariable(name, value);
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Collection<GValue<?>> gValues = super.getGValues();
+        if (from instanceof GValueConstantTraversal && ((GValueConstantTraversal<Edge, String>) from).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<Edge, String>) from).getGValue());
+        }
+        if (to instanceof GValueConstantTraversal && ((GValueConstantTraversal<Edge, String>) to).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<Edge, String>) to).getGValue());
+        }
+        return gValues;
+    }
+
+    @Override
+    public AddEdgeStartStepPlaceholder clone() {
+        final AddEdgeStartStepPlaceholder clone = (AddEdgeStartStepPlaceholder) super.clone();
+        if (from != null){
+            clone.from = from.clone();
+        }
+        if (to != null){
+            clone.to = to.clone();
+        }
+        return clone;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index 03419e8bcd..77d6dfcffa 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -18,14 +18,14 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
@@ -39,18 +39,13 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.Attachable;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
-import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
-        implements Writing<Event.EdgeAddedEvent>, TraversalParent, Scoping, FromToModulating {
+        implements AddEdgeStepContract<S>, Configuring {
 
     private static final String FROM = Graph.Hidden.hide("from");
     private static final String TO = Graph.Hidden.hide("to");
@@ -59,11 +54,6 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
     private CallbackRegistry<Event.EdgeAddedEvent> callbackRegistry;
 
     public AddEdgeStep(final Traversal.Admin traversal, final String edgeLabel) {
-        super(traversal);
-        this.parameters.set(this, T.label, GValue.of(null, edgeLabel));
-    }
-
-    public AddEdgeStep(final Traversal.Admin traversal, final GValue<String> edgeLabel) {
         super(traversal);
         this.parameters.set(this, T.label, edgeLabel);
     }
@@ -88,13 +78,6 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
         return this.parameters.getReferencedLabels();
     }
 
-    @Override
-    public HashSet<PopInstruction> getPopInstructions() {
-        final HashSet<PopInstruction> popInstructions = new HashSet<>();
-        popInstructions.addAll(TraversalParent.super.getPopInstructions());
-        return popInstructions;
-    }
-
     @Override
     public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
@@ -111,46 +94,22 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
     }
 
     @Override
-    protected Edge map(final Traverser.Admin<S> traverser) {
-        Object literalOrVar = this.parameters.get(traverser, T.label, () -> GValue.of(null, Edge.DEFAULT_LABEL)).get(0);
-        final GValue<String> edgeLabel = literalOrVar instanceof String ?
-                GValue.ofString(null, (String) literalOrVar) :
-                (GValue<String>) literalOrVar;
-
-        Object theTo;
-        try {
-            theTo = this.parameters.get(traverser, TO, traverser::get).get(0);
-            if (theTo != null && !GValue.instanceOf(theTo, GType.VERTEX)) {
-                theTo = new ReferenceVertex(theTo);
-            }
-        } catch (IllegalArgumentException e) { // as thrown by TraversalUtil.apply()
-            throw new IllegalStateException(String.format(
-                    "addE(%s) failed because the to() traversal (which should give a Vertex) failed with: %s",
-                    edgeLabel, e.getMessage()));
-        }
-
-        if (theTo == null)
-            throw new IllegalStateException(String.format(
-                    "The value given to addE(%s).to() must resolve to a Vertex or the ID of a Vertex present in the graph, but null was specified instead", edgeLabel));
+    public Object getElementId() {
+        List<Object> ids = this.parameters.get(T.id, null);
+        return ids.isEmpty() ? null : ids.get(0);
+    }
 
-        Object theFrom;
-        try {
-            theFrom  = this.parameters.get(traverser, FROM, traverser::get).get(0);
-            if (theFrom != null && !GValue.instanceOf(theFrom, GType.VERTEX)) {
-                theFrom = new ReferenceVertex(theFrom);
-            }
-        } catch (IllegalArgumentException e) { // as thrown by TraversalUtil.apply()
-            throw new IllegalStateException(String.format(
-                    "addE(%s) failed because the from() traversal (which should give a Vertex) failed with: %s",
-                    edgeLabel, e.getMessage()));
-        }
+    @Override
+    public void setElementId(Object elementId) {
+        configure(T.id, elementId);
+    }
 
-        if (theFrom == null)
-            throw new IllegalStateException(String.format(
-                    "The value given to addE(%s).from() must resolve to a Vertex or the ID of a Vertex present in the graph, but null was specified instead", edgeLabel));
+    @Override
+    protected Edge map(final Traverser.Admin<S> traverser) {
+        final String edgeLabel = this.parameters.get(traverser, T.label, () -> Edge.DEFAULT_LABEL).get(0);
 
-        Vertex toVertex = GValue.getFrom(theTo);
-        Vertex fromVertex = GValue.getFrom(theFrom);
+        Vertex toVertex = getAdjacentVertex(this.parameters, TO, traverser, edgeLabel);
+        Vertex fromVertex = getAdjacentVertex(this.parameters, FROM, traverser, edgeLabel);
 
         try {
             if (toVertex instanceof Attachable)
@@ -173,7 +132,7 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
         }
 
 
-        final Edge edge = fromVertex.addEdge(edgeLabel.get(), toVertex, this.parameters.getKeyValues(traverser, TO, FROM, T.label));
+        final Edge edge = fromVertex.addEdge(edgeLabel, toVertex, this.parameters.getKeyValues(traverser, TO, FROM, T.label));
         EventUtil.registerEdgeCreation(callbackRegistry, getTraversal(), edge);
         return edge;
     }
@@ -212,4 +171,50 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
         return clone;
     }
 
+    @Override
+    public Object getLabel() {
+        Object label = parameters.get(T.label, () -> Edge.DEFAULT_LABEL).get(0);
+        if (label instanceof ConstantTraversal) {
+            return ((ConstantTraversal<?, ?>) label).next();
+        }
+        return label;
+    }
+
+    @Override
+    public void addProperty(Object key, Object value) {
+        configure(key, value);
+    }
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return Collections.unmodifiableMap(parameters.getRaw(T.label, TO, FROM));
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (parameters.contains(k)) {
+            parameters.remove(k);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeElementId() {
+        if (this.parameters.contains(T.id)) {
+            this.parameters.remove(T.id);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object getFrom() {
+        return getAdjacentVertex(this.parameters, FROM);
+    }
+
+    @Override
+    public Object getTo() {
+        return getAdjacentVertex(this.parameters, TO);
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepContract.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepContract.java
new file mode 100644
index 0000000000..13ec8d493a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepContract.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import java.util.List;
+import java.util.function.Consumer;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
+
+public interface AddEdgeStepContract<S> extends TraversalParent, Scoping, FromToModulating, AddElementStepContract<S, Edge>, Writing<Event.EdgeAddedEvent> {
+    Object getFrom();
+    Object getTo();
+
+    default Object getAdjacentVertex(Parameters parameters, String key) {
+        Object vertex;
+        List<Object> values = parameters.get(key, () -> null);
+        vertex = values.isEmpty() ? null : values.get(0);
+        if (vertex instanceof ConstantTraversal) {
+            vertex = ((ConstantTraversal<?, ?>) vertex).next();
+        }
+        if (vertex != null && !(vertex instanceof Traversal) && !(vertex instanceof Vertex)) {
+            vertex = new ReferenceVertex(vertex);
+        }
+        return vertex;
+    }
+
+    default Vertex getAdjacentVertex(Parameters parameters, String key, Traverser.Admin<S> traverser, String edgeLabel) {
+        Object vertex;
+        try {
+            List<Object> values = parameters.get(traverser, key, traverser::get);
+            vertex = values.isEmpty() ? null : values.get(0);
+            if (vertex != null && !(vertex instanceof Vertex)) {
+                vertex = new ReferenceVertex(vertex);
+            }
+        } catch (IllegalArgumentException e) { // as thrown by TraversalUtil.apply()
+            throw new IllegalStateException(String.format(
+                    "addE(%s) failed because the %s() traversal (which should give a Vertex) failed with: %s",
+                    edgeLabel, key, e.getMessage()));
+        }
+
+        if (vertex == null)
+            throw new IllegalStateException(String.format(
+                    "The value given to addE(%s).%s() must resolve to a Vertex or the ID of a Vertex present in the graph, but null was specified instead", edgeLabel, key));
+        return (Vertex) vertex;
+    }
+
+    default Object resolveVertexTraversal(Traversal.Admin<?,?> vertexTraversal) {
+        return resolveVertexTraversal(vertexTraversal, null);
+    }
+
+    default Object resolveVertexTraversal(Traversal.Admin<?,?> vertexTraversal, Consumer<GValue<?>> gValueConsumer) {
+        if (vertexTraversal instanceof GValueConstantTraversal) {
+            if (gValueConsumer != null) {
+                gValueConsumer.accept(((GValueConstantTraversal<?, ?>) vertexTraversal).getGValue());
+            }
+            return new ReferenceVertex(vertexTraversal.next());
+        }
+        if (vertexTraversal instanceof ConstantTraversal) {
+            return new ReferenceVertex(vertexTraversal.next());
+        }
+        // anything other than a ConstantTraversal is returned as-is
+        return vertexTraversal;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepPlaceholder.java
new file mode 100644
index 0000000000..c008b8cbab
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepPlaceholder.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import java.util.Collection;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+
+public class AddEdgeStepPlaceholder<S> extends AbstractAddElementStepPlaceholder<S, Edge, Event.EdgeAddedEvent>
+        implements AddEdgeStepContract<S> {
+
+    private Traversal.Admin<?,?> from;
+    private Traversal.Admin<?,?> to;
+
+    public AddEdgeStepPlaceholder(final Traversal.Admin traversal, final String edgeLabel) {
+        super(traversal, edgeLabel == null ? Edge.DEFAULT_LABEL : edgeLabel);
+    }
+
+    public AddEdgeStepPlaceholder(final Traversal.Admin traversal, final GValue<String> edgeLabel) {
+        super(traversal, edgeLabel == null || edgeLabel.isNull() ? GValue.of(Edge.DEFAULT_LABEL) : edgeLabel);
+    }
+
+    public AddEdgeStepPlaceholder(final Traversal.Admin traversal, final Traversal.Admin<S,String> edgeLabelTraversal) {
+        super(traversal, edgeLabelTraversal == null ?
+                new ConstantTraversal<>(Edge.DEFAULT_LABEL) : edgeLabelTraversal);
+    }
+
+    @Override
+    public void addTo(final Traversal.Admin<?, ?> toObject) {
+        addTraversal(toObject);
+        if (toObject instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, ?>) toObject).getGValue());
+        }
+        this.to = toObject;
+    }
+
+    @Override
+    public void addFrom(final Traversal.Admin<?, ?> fromObject) {
+        addTraversal(fromObject);
+        if (fromObject instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, ?>) fromObject).getGValue());
+        }
+        this.from = fromObject;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        if (from != null) {
+            hash ^= from.hashCode();
+        }
+        if (to != null) {
+            hash ^= to.hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public AddEdgeStep<S> asConcreteStep() {
+        AddEdgeStep<S> step = new AddEdgeStep<>(traversal, label instanceof GValueConstantTraversal ? ((GValueConstantTraversal<S, String>) label).getConstantTraversal() : label);
+        super.configureConcreteStep(step);
+        if (from != null) {
+            step.addFrom(from instanceof GValueConstantTraversal ? ((GValueConstantTraversal<S, String>) from).getConstantTraversal() : from);
+        }
+        if (to != null) {
+            step.addTo(to instanceof GValueConstantTraversal ? ((GValueConstantTraversal<S, String>) to).getConstantTraversal() : to);
+        }
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        if (super.isParameterized() ||
+                (from instanceof GValueConstantTraversal && ((GValueConstantTraversal<S, String>) from).isParameterized()) ||
+                (to instanceof GValueConstantTraversal && ((GValueConstantTraversal<S, String>) to).isParameterized())) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean supportsMultiProperties() {
+        return false;
+    }
+
+    @Override
+    public Object getFrom() {
+        return resolveVertexTraversal(from, gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Object getFromGValueSafe() {
+        return resolveVertexTraversal(from);
+    }
+
+    @Override
+    public Object getTo() {
+        return resolveVertexTraversal(to, gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Object getToGValueSafe() {
+        return resolveVertexTraversal(to);
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        super.updateVariable(name, value);
+        if (from instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<S, String>) from).updateVariable(name, value);
+        }
+        if (to instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<S, String>) to).updateVariable(name, value);
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Collection<GValue<?>> gValues = super.getGValues();
+        if (from instanceof GValueConstantTraversal && ((GValueConstantTraversal<S, String>) from).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<S, String>) from).getGValue());
+        }
+        if (to instanceof GValueConstantTraversal && ((GValueConstantTraversal<S, String>) to).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<S, String>) to).getGValue());
+        }
+        return gValues;
+    }
+
+    @Override
+    public AddEdgeStepPlaceholder<S> clone() {
+        final AddEdgeStepPlaceholder<S> clone = (AddEdgeStepPlaceholder) super.clone();
+        if (from != null){
+            clone.from = from.clone();
+        }
+        if (to != null){
+            clone.to = to.clone();
+        }
+        return clone;
+    }
+}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddElementStepContract.java
similarity index 57%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddElementStepContract.java
index d44bde2ce0..6c3bc8cc97 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddElementStepContract.java
@@ -18,24 +18,24 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Scope;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PropertyAdding;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 
-import java.util.Arrays;
-import java.util.List;
+import java.util.HashSet;
 
-/**
- * @author Daniel Kuppitz (http://gremlin.guru)
- */
-public class TailLocalStepTest extends StepTest {
+public interface AddElementStepContract<S, E> extends Step<S, E>, PropertyAdding, TraversalParent, Scoping {
+    Object getLabel();
+
+    Object getElementId();
+
+    void setElementId(Object elementId);
+
+    boolean removeElementId();
 
     @Override
-    protected List<Traversal> getTraversals() {
-        return Arrays.asList(
-                __.tail(Scope.local, 5L),
-                __.tail(Scope.local, 10L)
-        );
+    default HashSet<PopInstruction> getPopInstructions() {
+        return TraversalParent.super.getPopInstructions();
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index c7a27e2eb8..fd91c769af 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -18,14 +18,18 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
@@ -38,16 +42,12 @@ import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
-        implements Writing<Event.VertexAddedEvent>, TraversalParent, Scoping {
+        implements TraversalParent, Scoping, AddVertexStepContract<Vertex>, Configuring {
 
     private Parameters parameters = new Parameters();
     private boolean first = true;
@@ -66,16 +66,22 @@ public class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
         userProvidedLabel = vertexLabelTraversal != null;
     }
 
-    public AddVertexStartStep(final Traversal.Admin traversal, final GValue<String> label) {
-        super(traversal);
-        this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
-        userProvidedLabel = label != null;
-    }
-
+    @Override
     public boolean hasUserProvidedLabel() {
         return userProvidedLabel;
     }
 
+    @Override
+    public Object getElementId() {
+        List<Object> ids = this.parameters.get(T.id, null);
+        return ids.isEmpty() ? null : ids.get(0);
+    }
+
+    @Override
+    public void setElementId(Object elementId) {
+        configure(T.id, elementId);
+    }
+
     @Override
     public Parameters getParameters() {
         return this.parameters;
@@ -86,13 +92,6 @@ public class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
         return this.parameters.getReferencedLabels();
     }
 
-    @Override
-    public HashSet<PopInstruction> getPopInstructions() {
-        final HashSet<PopInstruction> popInstructions = new HashSet<>();
-        popInstructions.addAll(TraversalParent.super.getPopInstructions());
-        return popInstructions;
-    }
-
     @Override
     public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
         return this.parameters.getTraversals();
@@ -167,4 +166,41 @@ public class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
         clone.userProvidedLabel = this.userProvidedLabel;
         return clone;
     }
+
+    @Override
+    public Object getLabel() {
+        Object label = parameters.get(T.label, () -> Vertex.DEFAULT_LABEL).get(0);
+        if (label instanceof ConstantTraversal) {
+            return ((ConstantTraversal<?, ?>) label).next();
+        }
+        return label;
+    }
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return Collections.unmodifiableMap(parameters.getRaw());
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (parameters.contains(k)) {
+            parameters.remove(k);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeElementId() {
+        if (this.parameters.contains(T.id)) {
+            this.parameters.remove(T.id);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void addProperty(final Object key, final Object value) {
+        configure(key, value);
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStepPlaceholder.java
new file mode 100644
index 0000000000..def5989635
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStepPlaceholder.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+public class AddVertexStartStepPlaceholder extends AbstractAddElementStepPlaceholder<Vertex, Vertex, Event.VertexAddedEvent>
+        implements AddVertexStepContract<Vertex>, GValueHolder<Vertex, Vertex> {
+
+    private boolean userProvidedLabel;
+
+    public AddVertexStartStepPlaceholder(final Traversal.Admin traversal, final String label) {
+        super(traversal, label == null ? Vertex.DEFAULT_LABEL : label);
+        userProvidedLabel = label != null;
+    }
+
+    public AddVertexStartStepPlaceholder(final Traversal.Admin traversal, final GValue<String> label) {
+        super(traversal, label == null ? GValue.of(Vertex.DEFAULT_LABEL) : label);
+        userProvidedLabel = label != null;
+    }
+
+    public AddVertexStartStepPlaceholder(final Traversal.Admin traversal, final Traversal.Admin<?,String> vertexLabelTraversal) {
+        super(traversal, vertexLabelTraversal == null ?
+                new ConstantTraversal<>(Vertex.DEFAULT_LABEL) : (Traversal.Admin<Vertex,String>) vertexLabelTraversal);
+        userProvidedLabel = vertexLabelTraversal != null;
+    }
+
+    @Override
+    public boolean hasUserProvidedLabel() {
+        return userProvidedLabel;
+    }
+
+    @Override
+    public AddVertexStartStepPlaceholder clone() {
+        final AddVertexStartStepPlaceholder clone = (AddVertexStartStepPlaceholder) super.clone();
+        clone.userProvidedLabel = this.userProvidedLabel;
+        return clone;
+    }
+
+    @Override
+    protected boolean supportsMultiProperties() {
+        return true;
+    }
+
+    @Override
+    public AddVertexStartStep asConcreteStep() {
+        AddVertexStartStep step = new AddVertexStartStep(traversal, label instanceof GValueConstantTraversal ? ((GValueConstantTraversal<?, String>) label).getConstantTraversal() : label);
+        super.configureConcreteStep(step);
+        return step;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index bcf21c7bc7..b3baa8b729 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -20,7 +20,8 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
@@ -34,8 +35,9 @@ import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -43,7 +45,7 @@ import java.util.Set;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
-        implements Writing<Event.VertexAddedEvent>, TraversalParent, Scoping {
+        implements Writing<Event.VertexAddedEvent>, TraversalParent, Scoping, AddVertexStepContract<S>, Configuring {
 
     private Parameters parameters = new Parameters();
     private CallbackRegistry<Event.VertexAddedEvent> callbackRegistry;
@@ -54,10 +56,6 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
         this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
         userProvidedLabel = label != null;
     }
-    public AddVertexStep(final Traversal.Admin traversal, final GValue<String> label) {
-        super(traversal);
-        this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
-    }
 
     public AddVertexStep(final Traversal.Admin traversal, final Traversal.Admin<S,String> vertexLabelTraversal) {
         super(traversal);
@@ -65,10 +63,22 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
         userProvidedLabel = vertexLabelTraversal != null;
     }
 
+    @Override
     public boolean hasUserProvidedLabel() {
         return userProvidedLabel;
     }
 
+    @Override
+    public Object getElementId() {
+        List<Object> ids = this.parameters.get(T.id, null);
+        return ids.isEmpty() ? null : ids.get(0);
+    }
+
+    @Override
+    public void setElementId(Object elementId) {
+        configure(T.id, elementId);
+    }
+
     @Override
     public Parameters getParameters() {
         return this.parameters;
@@ -79,13 +89,6 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
         return this.parameters.getReferencedLabels();
     }
 
-    @Override
-    public HashSet<PopInstruction> getPopInstructions() {
-        final HashSet<PopInstruction> popInstructions = new HashSet<>();
-        popInstructions.addAll(TraversalParent.super.getPopInstructions());
-        return popInstructions;
-    }
-
     @Override
     public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
         return this.parameters.getTraversals();
@@ -152,4 +155,41 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
         clone.userProvidedLabel = this.userProvidedLabel;
         return clone;
     }
+
+    @Override
+    public Object getLabel() {
+        Object label = parameters.get(T.label, () -> Vertex.DEFAULT_LABEL).get(0);
+        if (label instanceof ConstantTraversal) {
+            return ((ConstantTraversal<?, ?>) label).next();
+        }
+        return label;
+    }
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return Collections.unmodifiableMap(parameters.getRaw());
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (parameters.contains(k)) {
+            parameters.remove(k);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeElementId() {
+        if (this.parameters.contains(T.id)) {
+            this.parameters.remove(T.id);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void addProperty(final Object key, final Object value) {
+        configure(key, value);
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepContract.java
similarity index 62%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepContract.java
index 14553d9e62..c97845b1f0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepContract.java
@@ -16,22 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-package org.apache.tinkerpop.gremlin.process.traversal.step;
-
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
 
-/**
- * A marker interface for steps that modify the graph.
- *
- * @author Stephen Mallette (http://stephen.genoprime.com)
- * @author Matt Frantz (http://github.com/mhfrantz)
- */
-public interface Mutating<E extends Event> extends Configuring {
+public interface AddVertexStepContract<S> extends Step<S, Vertex>, AddElementStepContract<S, Vertex>, Writing<Event.VertexAddedEvent> {
 
-    /**
-     * Gets the callback registry for events that the step raises.
-     */
-    public CallbackRegistry<E> getMutatingCallbackRegistry();
+    boolean hasUserProvidedLabel();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepPlaceholder.java
new file mode 100644
index 0000000000..77315ca580
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepPlaceholder.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+public class AddVertexStepPlaceholder<S> extends AbstractAddElementStepPlaceholder<S, Vertex, Event.VertexAddedEvent>
+        implements AddVertexStepContract<S>, GValueHolder<S, Vertex> {
+
+    private boolean userProvidedLabel;
+
+    public AddVertexStepPlaceholder(final Traversal.Admin traversal, final String label) {
+        super(traversal, label == null ? Vertex.DEFAULT_LABEL : label);
+        userProvidedLabel = label != null;
+    }
+
+    public AddVertexStepPlaceholder(final Traversal.Admin traversal, final GValue<String> label) {
+        super(traversal, label == null ? GValue.of(Vertex.DEFAULT_LABEL) : label);
+        userProvidedLabel = label != null;
+    }
+
+    public AddVertexStepPlaceholder(final Traversal.Admin traversal, final Traversal.Admin<S,String> vertexLabelTraversal) {
+        super(traversal, vertexLabelTraversal == null ?
+                new ConstantTraversal<>(Vertex.DEFAULT_LABEL) : vertexLabelTraversal);
+        userProvidedLabel = vertexLabelTraversal != null;
+    }
+
+    @Override
+    public boolean hasUserProvidedLabel() {
+        return userProvidedLabel;
+    }
+
+    @Override
+    public AddVertexStepPlaceholder<S> clone() {
+        final AddVertexStepPlaceholder<S> clone = (AddVertexStepPlaceholder<S>) super.clone();
+        clone.userProvidedLabel = this.userProvidedLabel;
+        return clone;
+    }
+
+    @Override
+    protected boolean supportsMultiProperties() {
+        return true;
+    }
+
+    @Override
+    public AddVertexStep<S> asConcreteStep() {
+        AddVertexStep<S> step = new AddVertexStep<>(traversal, label instanceof GValueConstantTraversal ? ((GValueConstantTraversal<S, String>) label).getConstantTraversal() : label);
+        super.configureConcreteStep(step);
+        return step;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
index ea909b5606..2beb54a3ce 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
@@ -21,9 +21,6 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -55,7 +52,7 @@ import static org.apache.tinkerpop.gremlin.structure.service.Service.ServiceCall
  *
  * @author Mike Personick (http://github.com/mikepersonick)
  */
-public final class CallStep<S, E> extends AbstractStep<S, E> implements TraversalParent, AutoCloseable, Configuring {
+public final class CallStep<S, E> extends AbstractStep<S, E> implements AutoCloseable, CallStepContract<S, E> {
 
     private final boolean isStart;
     private boolean first = true;
@@ -63,7 +60,7 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
     private transient ServiceCallContext ctx;
     private final String serviceName;
     private transient Service<S, E> service;
-    private GValue<Map> staticParams;
+    private final Map staticParams;
     private Traversal.Admin<S,Map> mapTraversal;
     private Parameters parameters;
 
@@ -75,50 +72,39 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
     }
 
     public CallStep(final Traversal.Admin traversal, final boolean isStart, final String service) {
-        this(traversal, isStart, service, (Map) null);
+        this(traversal, isStart, service, null);
     }
 
     public CallStep(final Traversal.Admin traversal, final boolean isStart, final String service, final Map staticParams) {
-        this(traversal, isStart, service, GValue.ofMap(null, staticParams));
-    }
-
-    public CallStep(final Traversal.Admin traversal, final boolean isStart, final String service, final GValue<Map> staticParams) {
         this(traversal, isStart, service, staticParams, null);
     }
 
     public CallStep(final Traversal.Admin traversal, final boolean isStart, final String service, final Map staticParams,
                     final Traversal.Admin<S, Map> mapTraversal) {
-        this(traversal, isStart, service, GValue.ofMap(null, staticParams), mapTraversal);
-    }
-
-    public CallStep(final Traversal.Admin traversal, final boolean isStart, final String service, final GValue<Map> staticParams,
-                    final Traversal.Admin<S, Map> mapTraversal) {
         super(traversal);
 
         this.isStart = isStart;
         this.serviceName = service;
-        this.staticParams = staticParams == null || staticParams.isNull() ? GValue.ofMap(staticParams.getName(), new LinkedHashMap()) : staticParams;
+        this.staticParams = staticParams == null ? new LinkedHashMap() : staticParams;
         this.mapTraversal = mapTraversal == null ? null : integrateChild(mapTraversal);
         this.parameters = new Parameters();
     }
 
-    protected Service<S, E> service() {
+    @Override
+    public Service<S, E> service() {
         // throws exception for unrecognized service
-        return service != null ? service : (service = getServiceRegistry().get(serviceName, isStart, staticParams.get()));
+        return service != null ? service : (service = getServiceRegistry().get(serviceName, isStart, staticParams));
     }
 
     private ServiceCallContext ctx() {
         return ctx != null ? ctx : (ctx = new ServiceCallContext(traversal, this));
     }
 
+    @Override
     public String getServiceName() {
         return serviceName;
     }
 
-    public GValue<Map> getStaticParams() {
-        return staticParams;
-    }
-
     @Override
     protected Traverser.Admin<E> processNextStart() {
         if (isStart && first) {
@@ -213,10 +199,11 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
         this.iterator = EmptyCloseableIterator.instance();
     }
 
+    @Override
     public Map getMergedParams() {
         if (mapTraversal == null && parameters.isEmpty()) {
             // static params only
-            return Collections.unmodifiableMap(this.staticParams.get());
+            return Collections.unmodifiableMap(this.staticParams);
         }
 
         return getMergedParams(new DummyTraverser(this.traversal.getTraverserGenerator()));
@@ -225,11 +212,11 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
     protected Map getMergedParams(final Traverser.Admin<S> traverser) {
         if (mapTraversal == null && parameters.isEmpty()) {
             // static params only
-            return Collections.unmodifiableMap(this.staticParams.get());
+            return Collections.unmodifiableMap(this.staticParams);
         }
 
         // merge dynamic with static params
-        final Map params = new LinkedHashMap(this.staticParams.get());
+        final Map params = new LinkedHashMap(this.staticParams);
         if (mapTraversal != null) params.putAll(TraversalUtil.apply(traverser, mapTraversal));
         final Object[] kvs = this.parameters.getKeyValues(traverser);
         for (int i = 0; i < kvs.length; i += 2) {
@@ -242,7 +229,7 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
     protected Map getMergedParams(final TraverserSet<S> traverserSet) {
         if (mapTraversal == null && parameters.isEmpty()) {
             // static params only
-            return Collections.unmodifiableMap(this.staticParams.get());
+            return Collections.unmodifiableMap(this.staticParams);
         }
 
         /*
@@ -274,7 +261,8 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
         return service().execute(this.ctx(), traverserSet, params);
     }
 
-    protected ServiceRegistry getServiceRegistry() {
+    @Override
+    public ServiceRegistry getServiceRegistry() {
         final Graph graph = (Graph) this.traversal.getGraph().get();
         return graph.getServiceRegistry();
     }
@@ -319,7 +307,7 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
     public String toString() {
         final ArrayList args = new ArrayList();
         args.add(serviceName);
-        if (!staticParams.get().isEmpty())
+        if (!staticParams.isEmpty())
             args.add(staticParams);
         if (mapTraversal != null)
             args.add(mapTraversal);
@@ -331,8 +319,8 @@ public final class CallStep<S, E> extends AbstractStep<S, E> implements Traversa
     @Override
     public int hashCode() {
         int hashCode = super.hashCode() ^ Objects.hashCode(this.serviceName);
-        if (!staticParams.get().isEmpty())
-            hashCode ^= staticParams.get().hashCode();
+        if (!staticParams.isEmpty())
+            hashCode ^= staticParams.hashCode();
         if (mapTraversal != null)
             hashCode ^= mapTraversal.hashCode();
         if (!parameters.isEmpty())
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepContract.java
similarity index 55%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepContract.java
index d44bde2ce0..57e0559840 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepContract.java
@@ -18,24 +18,26 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Scope;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.structure.service.Service;
+import org.apache.tinkerpop.gremlin.structure.service.ServiceRegistry;
 
-import java.util.Arrays;
-import java.util.List;
+import java.util.Map;
 
-/**
- * @author Daniel Kuppitz (http://gremlin.guru)
- */
-public class TailLocalStepTest extends StepTest {
+public interface CallStepContract<S, E> extends Step<S, E>, Configuring, TraversalParent {
+    Service<S, E> service();
+
+    String getServiceName();
+
+    /**
+     * TODO: re-evaluate if we should change this to return the static params only
+     */
+    Map getMergedParams();
+
+    ServiceRegistry getServiceRegistry();
 
     @Override
-    protected List<Traversal> getTraversals() {
-        return Arrays.asList(
-                __.tail(Scope.local, 5L),
-                __.tail(Scope.local, 10L)
-        );
-    }
+    void reset();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepPlaceholder.java
new file mode 100644
index 0000000000..08c055aa72
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepPlaceholder.java
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.DummyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.service.Service;
+import org.apache.tinkerpop.gremlin.structure.service.ServiceRegistry;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+
+/**
+ * Reference implementation for service calls via the {@link ServiceRegistry} and {@link Service} APIs. This step
+ * can be used to start a traversal or it can be used mid-traversal with one at a time or barrier execution.
+ *
+ * @author Mike Personick (http://github.com/mikepersonick)
+ */
+public final class CallStepPlaceholder<S, E> extends AbstractStep<S, E> implements CallStepContract<S, E>, GValueHolder<S, E> {
+
+    private final boolean isStart;
+
+    private final String serviceName;
+    private GValue<Map<?,?>> staticParams;
+    private Traversal.Admin<S,Map> mapTraversal;
+    private Parameters parameters;
+
+    public CallStepPlaceholder(final Traversal.Admin traversal, final boolean isStart) {
+        this(traversal, isStart, null);
+    }
+
+    public CallStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final String service) {
+        this(traversal, isStart, service, null);
+    }
+
+    public CallStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final String service, final GValue<Map<?,?>> staticParams) {
+        this(traversal, isStart, service, staticParams, null);
+    }
+
+    public CallStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final String service, final GValue<Map<?,?>> staticParams,
+                               final Traversal.Admin<S, Map<?,?>> mapTraversal) {
+        super(traversal);
+
+        this.isStart = isStart;
+        this.serviceName = service;
+        this.staticParams = staticParams == null ? GValue.of(new LinkedHashMap()) : staticParams;
+        this.mapTraversal = mapTraversal == null ? null : integrateChild(mapTraversal);
+        this.parameters = new Parameters();
+
+        if (this.staticParams.isVariable()) {
+            traversal.getGValueManager().register(this.staticParams);
+        }
+    }
+
+    @Override
+    public Service<S, E> service() {
+        // throws exception for unrecognized service
+        if(staticParams.isVariable()) {
+            traversal.getGValueManager().pinVariable(staticParams.getName());
+        }
+        return getServiceRegistry().get(serviceName, isStart, staticParams.get());
+    }
+
+    public Service<S, E> serviceGValueSafe() {
+        // throws exception for unrecognized service
+        return getServiceRegistry().get(serviceName, isStart, staticParams.get());
+    }
+
+    @Override
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    @Override
+    protected Traverser.Admin<E> processNextStart() {
+        throw new IllegalStateException("GValuePlaceholder step is not executable");
+    }
+
+    @Override
+    public Map getMergedParams() {
+        if (mapTraversal == null && parameters.isEmpty()) {
+            // static params only
+            if (staticParams.isVariable()) {
+                traversal.getGValueManager().pinVariable(staticParams.getName());
+            }
+            return Collections.unmodifiableMap(this.staticParams.get());
+        }
+
+        return getMergedParams(new DummyTraverser<>(this.traversal.getTraverserGenerator()), 
+                gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Map getMergedParamsGValueSafe() {
+        if (mapTraversal == null && parameters.isEmpty()) {
+            // static params only
+            return Collections.unmodifiableMap(this.staticParams.get());
+        }
+        return getMergedParams(new DummyTraverser<>(this.traversal.getTraverserGenerator()), null);
+    }
+
+    protected Map getMergedParams(final Traverser.Admin<S> traverser, Consumer<GValue> gValueConsumer) {
+        if (mapTraversal == null && parameters.isEmpty()) {
+            // static params only
+            if (gValueConsumer != null && staticParams.isVariable()) {
+                gValueConsumer.accept(staticParams);
+            }
+            return Collections.unmodifiableMap(this.staticParams.get());
+        }
+
+        // merge dynamic with static params
+        if(gValueConsumer != null && staticParams.isVariable()) {
+            gValueConsumer.accept(staticParams);
+        }
+        final Map params = new LinkedHashMap(this.staticParams.get());
+        if (mapTraversal != null) params.putAll(TraversalUtil.apply(traverser, mapTraversal));
+        final Object[] kvs = this.parameters.getKeyValues(traverser);
+        for (int i = 0; i < kvs.length; i += 2) {
+            // this will overwrite any multi-valued kvs
+            params.put(kvs[i], kvs[i + 1]);
+        }
+        return params;
+    }
+
+    @Override
+    public ServiceRegistry getServiceRegistry() {
+        final Graph graph = (Graph) this.traversal.getGraph().get();
+        return graph.getServiceRegistry();
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+        if (mapTraversal != null)
+            mapTraversal.reset();
+        parameters.getTraversals().forEach(Traversal.Admin::reset);
+    }
+
+    @Override
+    public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
+        final List<Traversal.Admin<S, E>> children = new ArrayList<>();
+        if (mapTraversal != null) children.add((Traversal.Admin<S, E>) mapTraversal);
+        children.addAll(parameters.getTraversals());
+        return children;
+    }
+
+    @Override
+    public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) {
+        super.setTraversal(parentTraversal);
+        if (mapTraversal != null)
+            this.integrateChild(mapTraversal);
+        parameters.getTraversals().forEach(this::integrateChild);
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        final Set<TraverserRequirement> requirements = this.getSelfAndChildRequirements();
+        requirements.addAll(service().getRequirements());
+        return requirements;
+    }
+
+    @Override
+    public String toString() {
+        final ArrayList args = new ArrayList();
+        args.add(serviceName);
+        if (!staticParams.get().isEmpty())
+            args.add(staticParams);
+        if (mapTraversal != null)
+            args.add(mapTraversal);
+        if (!parameters.isEmpty())
+            args.add(parameters);
+        return StringFactory.stepString(this, args.toArray());
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = super.hashCode() ^ Objects.hashCode(this.serviceName);
+        if (!staticParams.get().isEmpty())
+            hashCode ^= staticParams.hashCode();
+        if (mapTraversal != null)
+            hashCode ^= mapTraversal.hashCode();
+        if (!parameters.isEmpty())
+            hashCode ^= parameters.hashCode();
+        return hashCode;
+    }
+
+    @Override
+    public CallStepPlaceholder<S, E> clone() {
+        final CallStepPlaceholder<S, E> clone = (CallStepPlaceholder<S, E>) super.clone();
+        clone.mapTraversal = mapTraversal != null ? mapTraversal.clone() : null;
+        clone.parameters = parameters.clone();
+        return clone;
+    }
+
+    @Override
+    public void configure(final Object... keyValues) {
+        this.parameters.set(this, keyValues);
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return this.parameters;
+    }
+
+    @Override
+    public CallStep<S, E> asConcreteStep() {
+        CallStep<S, E> step = new CallStep<>(traversal, isStart, serviceName, staticParams.get(), mapTraversal);
+        for (Map.Entry<Object, List<Object>> entry : parameters.getRaw().entrySet()) {
+            for (Object value : entry.getValue()) {
+                step.configure(entry.getKey(), value);
+            }
+        }
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return staticParams.isVariable();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (name.equals(staticParams.getName())) {
+            staticParams = GValue.of(name, (Map<?,?>) value);
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        return staticParams.isVariable() ? List.of(staticParams) : Collections.emptyList();
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
index 41bce05417..0d4db215fa 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
@@ -20,7 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.tinkerpop.gremlin.process.traversal.Compare;
-import org.apache.tinkerpop.gremlin.process.traversal.Contains;             
+import org.apache.tinkerpop.gremlin.process.traversal.Contains;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -43,21 +43,18 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.function.Supplier;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Pieter Martin
  */
-public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implements GraphComputing, AutoCloseable, Configuring {
+public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implements GraphComputing, AutoCloseable, Configuring, GraphStepContract<S, E> {
 
     protected Parameters parameters = new Parameters();
     protected final Class<E> returnClass;
-    protected GValue<?>[] ids;
-    protected boolean legacyLogicForPassingNoIds = false;
+    protected Object[] ids;
     protected transient Supplier<Iterator<E>> iteratorSupplier;
     protected boolean isStart;
     protected boolean done = false;
@@ -68,41 +65,17 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
     public GraphStep(final Traversal.Admin traversal, final Class<E> returnClass, final boolean isStart, final Object... ids) {
         super(traversal);
         this.returnClass = returnClass;
+        this.ids = (ids != null && ids.length == 1 && ids[0] instanceof Collection) ? ((Collection) ids[0]).toArray(new Object[((Collection) ids[0]).size()]) : ids;
 
-        // if ids is a single collection like g.V(['a','b','c']), then unroll it into an array of ids
-        this.ids = GValue.ensureGValues(tryUnrollSingleCollectionArgument(ids));
-
+        for (Object id : this.ids) {
+            if (id instanceof GValue) {
+                throw new IllegalArgumentException("GValue Not Allowed as id in GraphStep");
+            }
+        }
         this.isStart = isStart;
-
-        Object[] idValues = GValue.resolveToValues(this.ids);
         this.iteratorSupplier = () -> (Iterator<E>) (Vertex.class.isAssignableFrom(this.returnClass) ?
-                this.getTraversal().getGraph().get().vertices(idValues) :
-                this.getTraversal().getGraph().get().edges(idValues));
-    }
-
-    /**
-     * Unrolls a single collection argument into an array of ids. This is useful for steps like
-     * {@code g.V(['a','b','c'])}.
-     */
-    protected static Object[] tryUnrollSingleCollectionArgument(final Object[] ids) {
-        final Object[] tempIds;
-        if (ids != null && ids.length == 1) {
-            final Optional<Collection> opt;
-            if (ids[0] instanceof GValue && ((GValue<?>) ids[0]).getType().isCollection())
-                opt = Optional.of((Collection) ((GValue) ids[0]).get());
-            else if (ids[0] instanceof Collection)
-                opt = Optional.of((Collection) ids[0]);
-            else
-                opt = Optional.empty();
-
-            if (opt.isPresent()) {
-                tempIds = opt.get().toArray(new Object[opt.get().size()]);
-            } else
-                tempIds = ids;
-        } else {
-            tempIds = ids;
-        }
-        return tempIds;
+                this.getTraversal().getGraph().get().vertices(this.ids) :
+                this.getTraversal().getGraph().get().edges(this.ids));
     }
 
     public String toString() {
@@ -119,10 +92,12 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
         this.parameters.set(null, keyValues);
     }
 
+    @Override
     public Class<E> getReturnClass() {
         return this.returnClass;
     }
 
+    @Override
     public boolean isStartStep() {
         return this.isStart;
     }
@@ -131,10 +106,12 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
         return step instanceof GraphStep && ((GraphStep) step).isStart;
     }
 
+    @Override
     public boolean returnsVertex() {
         return this.returnClass.equals(Vertex.class);
     }
 
+    @Override
     public boolean returnsEdge() {
         return this.returnClass.equals(Edge.class);
     }
@@ -143,39 +120,26 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
         this.iteratorSupplier = iteratorSupplier;
     }
 
-    /**
-     * Get the ids associated with this step. If there are {@link GValue} objects present they will be returned
-     * alongside literal ids. Prefer {@link #getIdsAsValues()} if you prefer to work with literal ids only.
-     */
-    public GValue[] getIds() {
+    @Override
+    public Object[] getIds() {
         return this.ids;
     }
 
-    /**
-     * Gets the ids associated with this step as literal values rather than {@link GValue} objects.
-     */
-    public Object[] getIdsAsValues() {
-        if (legacyLogicForPassingNoIds) return null;
-        return GValue.resolveToValues(this.ids);
-    }
-
     public void addIds(final Object... newIds) {
-        // there is some logic that has been around for a long time that used to set ids to null. it only happened here
-        // in this method and only occurred when the ids were already null or empty and the newIds were length 1 and
-        // an instance of List and that list was empty. so basically it would trigger for something like g.V().hasId([])
-        // which in turn would trigger an empty iterator in TinkerGraphStep and zero results. trying to maintain that
-        // logic now with GValue in the mix is tough because the context of what the meaning is gets lost by the time
-        // you get to calling getResolvedIds(). using a flag to try to maintain that legacy logic, but ultimately, all
-        // this needs to be rethought.
-        this.legacyLogicForPassingNoIds = newIds.length == 1 && ((newIds[0] instanceof List && ((List) newIds[0]).isEmpty()) ||
-                (newIds[0] instanceof GValue && ((GValue) newIds[0]).getType().isCollection() && ((List) ((GValue) newIds[0]).get()).isEmpty()));
-
-        final GValue[] gvalues = GValue.ensureGValues(tryUnrollSingleCollectionArgument(newIds));
-        this.ids = ArrayUtils.addAll(this.ids, gvalues);
+        if ((this.ids == null || this.ids.length == 0) &&
+                newIds.length == 1 &&
+                newIds[0] instanceof Collection && ((Collection) newIds[0]).isEmpty())
+            this.ids = null;
+        else
+            this.ids = ArrayUtils.addAll(this.ids,
+                    (newIds.length == 1 && newIds[0] instanceof Collection) ?
+                            ((Collection) newIds[0]).toArray(new Object[((Collection) newIds[0]).size()]) :
+                            newIds);
     }
 
+    @Override
     public void clearIds() {
-        this.ids = new GValue[0];
+        this.ids = new Object[0];
     }
 
     @Override
@@ -186,12 +150,9 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
 
     public void convertElementsToIds() {
         if (null != this.ids) {
-            // if this is going to OLAP, convert to ids so you don't serialize elements
-            for (int i = 0; i < this.ids.length; i++) {
-                final GValue<?> current = this.ids[i];
-                if (current.get() instanceof Element) {
-                    this.ids[i] = GValue.of(current.getName(), ((Element) current.get()).id());
-                }
+            for (int i = 0; i < this.ids.length; i++) {    // if this is going to OLAP, convert to ids so you don't serialize elements
+                if (this.ids[i] instanceof Element)
+                    this.ids[i] = ((Element) this.ids[i]).id();
             }
         }
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStepContract.java
similarity index 61%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStepContract.java
index db205b6a6c..bbc9ae75e3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Ranging.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStepContract.java
@@ -16,15 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.tinkerpop.gremlin.process.traversal.step;
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public interface Ranging {
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GraphComputing;
+import org.apache.tinkerpop.gremlin.structure.Element;
+
+public interface GraphStepContract<S, E extends Element> extends Step<S, E>, GraphComputing {
+
+    Class<E> getReturnClass();
+
+    boolean isStartStep();
+
+    boolean returnsVertex();
 
-    public long getLowRange();
+    boolean returnsEdge();
 
-    public long getHighRange();
+    Object[] getIds();
 
+    void clearIds();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStepPlaceholder.java
new file mode 100644
index 0000000000..a8241d6ae7
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStepPlaceholder.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+public class GraphStepPlaceholder<S, E extends Element> extends AbstractStep<S, E> implements GValueHolder<S, E>, GraphStepContract<S, E> {
+
+    protected final Class<E> returnClass;
+    protected GValue<?>[] ids;
+    private final boolean isStart;
+    private boolean onGraphComputer = false;
+
+    public GraphStepPlaceholder(final Traversal.Admin traversal, final Class<E> returnClass, final boolean isStart, final GValue<?>... ids) {
+        super(traversal);
+        this.returnClass = returnClass;
+
+        this.ids = ids;
+        this.isStart = isStart;
+
+        for (GValue id : ids) {
+            traversal.getGValueManager().register(id);
+        }
+    }
+
+    public String toString() {
+        return StringFactory.stepString(this, this.returnClass.getSimpleName().toLowerCase(), Arrays.toString(this.ids));
+    }
+
+    @Override
+    public Class<E> getReturnClass() {
+        return this.returnClass;
+    }
+
+    @Override
+    public boolean isStartStep() {
+        return this.isStart;
+    }
+
+    @Override
+    public boolean returnsVertex() {
+        return this.returnClass.equals(Vertex.class);
+    }
+
+    @Override
+    public boolean returnsEdge() {
+        return this.returnClass.equals(Edge.class);
+    }
+
+    @Override
+    public Object[] getIds() {
+        for (final GValue<?> id : this.ids) {
+            if (id.isVariable()) {
+                traversal.getGValueManager().pinVariable(id.getName());
+            }
+        }
+        return GValue.resolveToValues(this.ids);
+    }
+
+    public Object[] getIdsGValueSafe() {
+        return GValue.resolveToValues(this.ids);
+    }
+
+    @Override
+    public void clearIds() {
+        this.ids = new GValue<?>[0];
+    }
+
+    @Override
+    public void onGraphComputer() {
+        this.onGraphComputer = true;
+    }
+
+    @Override
+    protected Traverser.Admin<E> processNextStart() {
+        throw new IllegalStateException("GraphStepGValueContract is not executable");
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(super.hashCode(), returnClass);
+        if (ids != null) {
+            for (Object id : ids) {
+                result = 31 * result + Objects.hashCode(id);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public GraphStep<S, E> asConcreteStep() {
+        GraphStep<S, E> step = new GraphStep<>(traversal, returnClass, isStart, GValue.resolveToValues(ids));
+
+        if (onGraphComputer) {
+            step.onGraphComputer();
+        }
+
+        TraversalHelper.copyLabels(this, step, false);
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        for (GValue<?> id : ids) {
+            if (id.isVariable()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        for (int i = 0; i < ids.length; i++) {
+            if (name.equals(ids[i].getName())) {
+                ids[i] = GValue.of(name, value);
+            }
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Set<GValue<?>> gValues = new HashSet<>();
+        for (GValue<?> id : ids) {
+            if (id.isVariable()) {
+                gValues.add(id);
+            }
+        }
+        return gValues;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
index 4f9712f9d2..c43c248b3b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
@@ -34,7 +34,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -53,7 +52,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outV;
 /**
  * Implementation for the {@code mergeE()} step covering both the start step version and the one used mid-traversal.
  */
-public class MergeEdgeStep<S> extends MergeElementStep<S, Edge, Object> {
+public class MergeEdgeStep<S> extends MergeElementStep<S, Edge, Map<Object, Object>> {
 
     private static final Set allowedTokens = new LinkedHashSet(Arrays.asList(T.id, T.label, Direction.IN, Direction.OUT));
 
@@ -68,15 +67,11 @@ public class MergeEdgeStep<S> extends MergeElementStep<S, Edge, Object> {
         super(traversal, isStart);
     }
 
-    public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Map merge) {
+    public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> merge) {
         super(traversal, isStart, merge);
     }
 
-    public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) {
-        super(traversal, isStart, merge);
-    }
-
-    public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) {
+    public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map<Object, Object>> mergeTraversal) {
         super(traversal, isStart, mergeTraversal);
     }
 
@@ -97,7 +92,7 @@ public class MergeEdgeStep<S> extends MergeElementStep<S, Edge, Object> {
     }
 
     @Override
-    public void addChildOption(final Merge token, final Traversal.Admin<S, Object> traversalOption) {
+    public void addChildOption(final Merge token, final Traversal.Admin<S, Map<Object, Object>> traversalOption) {
         if (token == Merge.outV) {
             this.outVTraversal = this.integrateChild(traversalOption);
         } else if (token == Merge.inV) {
@@ -430,5 +425,4 @@ public class MergeEdgeStep<S> extends MergeElementStep<S, Edge, Object> {
             return v;
         }
     }
-
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStepPlaceholder.java
new file mode 100644
index 0000000000..eb5887790f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStepPlaceholder.java
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueHelper;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Implementation for the {@code mergeV()} step covering both the start step version and the one used mid-traversal.
+ */
+public class MergeEdgeStepPlaceholder<S> extends AbstractStep<S, Edge> implements MergeStepContract<S, Edge, Map<Object, Object>>, GValueHolder<S, Edge> {
+
+    private static final Set allowedTokens = new LinkedHashSet(Arrays.asList(T.id, T.label, Direction.IN, Direction.OUT));
+    private Map<Object, List<Object>> properties = new HashMap<>();
+
+    public static void validateMapInput(final Map map, final boolean ignoreTokens) {
+        MergeElementStep.validate(map, ignoreTokens, allowedTokens, "mergeE");
+    }
+
+    private Traversal.Admin<?,Map<Object,Object>> mergeTraversal;
+    private Traversal.Admin<?,Map<Object,Object>> onCreateTraversal;
+    private Traversal.Admin<?,Map<Object,Object>> onMatchTraversal;
+    private Traversal.Admin<S,Map<Object,Object>> outVTraversal = null;
+    private Traversal.Admin<S,Map<Object,Object>> inVTraversal = null;
+    private final boolean isStart;
+
+    public MergeEdgeStepPlaceholder(final Traversal.Admin traversal, final boolean isStart) {
+        this(traversal, isStart, new IdentityTraversal<>());
+    }
+
+    public MergeEdgeStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> merge) {
+        this(traversal, isStart, new ConstantTraversal<>(merge));
+        validateMapInput(merge, false);
+    }
+
+    public MergeEdgeStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final GValue<Map<Object, Object>> merge) {
+        this(traversal, isStart, new GValueConstantTraversal<>(merge));
+        validateMapInput(merge.get(), false);
+    }
+
+    public MergeEdgeStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<?,Map<Object, Object>> mergeTraversal) {
+        super(traversal);
+        this.isStart = isStart;
+        this.mergeTraversal = mergeTraversal;
+        if (mergeTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getGValue());
+        }
+    }
+
+    @Override
+    protected Traverser.Admin<Edge> processNextStart() throws NoSuchElementException {
+        return null;
+    }
+
+    @Override
+    public MergeEdgeStepPlaceholder<S> clone() {
+        return (MergeEdgeStepPlaceholder<S>) super.clone();
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return super.getRequirements();
+    }
+
+    @Override
+    public Traversal.Admin getMergeTraversal() {
+        if (mergeTraversal != null && mergeTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getGValue().getName());
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getConstantTraversal();
+        }
+        return mergeTraversal;
+    }
+
+    public Traversal.Admin getMergeTraversalGValueSafe() {
+        if (mergeTraversal != null && mergeTraversal instanceof GValueConstantTraversal) {
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getConstantTraversal();
+        }
+        return mergeTraversal;
+    }
+
+    @Override
+    public Traversal.Admin getOnCreateTraversal() {
+        if (onCreateTraversal != null && onCreateTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).getGValue().getName());
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).getConstantTraversal();
+        }
+        return onCreateTraversal;
+    }
+
+    public Traversal.Admin getOnCreateTraversalGValueSafe() {
+        if (onCreateTraversal != null && onCreateTraversal instanceof GValueConstantTraversal) {
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).getConstantTraversal();
+        }
+        return onCreateTraversal;
+    }
+
+    @Override
+    public Traversal.Admin getOnMatchTraversal() {
+        if (onMatchTraversal != null && onMatchTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).getGValue().getName());
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).getConstantTraversal();
+        }
+        return onMatchTraversal;
+    }
+
+    public Traversal.Admin getOnMatchTraversalGValueSafe() {
+        if (onMatchTraversal != null && onMatchTraversal instanceof GValueConstantTraversal) {
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).getConstantTraversal();
+        }
+        return onMatchTraversal;
+    }
+
+    @Override
+    public boolean isStart() {
+        return isStart;
+    }
+
+    @Override
+    public boolean isFirst() {
+        return true; // Step cannot be iterated so isFirst() is always true
+    }
+
+    @Override
+    public void addChildOption(Merge token, Traversal.Admin traversalOption) {
+        if (token == Merge.onCreate) {
+            setOnCreate(traversalOption);
+        } else if (token == Merge.onMatch) {
+            setOnMatch(traversalOption);
+        } else if (token == Merge.outV) {
+            this.outVTraversal = traversalOption;
+        } else if (token == Merge.inV) {
+            this.inVTraversal = traversalOption;
+        }else {
+            throw new UnsupportedOperationException(String.format("Option %s for Merge is not supported", token.name()));
+        }
+    }
+
+    @Override
+    public boolean isUsingPartitionStrategy() {
+        return false;
+    }
+
+    @Override
+    public void setOnMatch(final Traversal.Admin<?,Map<Object, Object>> onMatchMap) {
+        this.onMatchTraversal = onMatchMap;
+        if (onMatchMap instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchMap).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) onMatchMap).getGValue());
+        }
+    }
+
+    @Override
+    public void setOnCreate(final Traversal.Admin<?,Map<Object, Object>> onCreateMap) {
+        this.onCreateTraversal = onCreateMap;
+        if (onCreateMap instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateMap).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) onCreateMap).getGValue());
+        }
+    }
+
+    @Override
+    public void setMerge(Traversal.Admin<?,Map<Object, Object>> mergeMap) {
+        this.mergeTraversal = mergeMap;
+        if (mergeMap instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) mergeMap).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) mergeMap).getGValue());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        MergeEdgeStepPlaceholder<?> that = (MergeEdgeStepPlaceholder<?>) o;
+        return isStart == that.isStart && Objects.equals(properties, that.properties) && Objects.equals(mergeTraversal, that.mergeTraversal) && Objects.equals(onCreateTraversal, that.onCreateTraversal) && Objects.equals(onMatchTraversal, that.onMatchTraversal) && Objects.equals(outVTraversal, that.outVTraversal) && Objects.equals(inVTraversal, that.inVTraversal);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), properties, mergeTraversal, onCreateTraversal, onMatchTraversal, outVTraversal, inVTraversal, isStart);
+    }
+
+    @Override
+    public MergeEdgeStep<S> asConcreteStep() {
+        MergeEdgeStep<S> step = new MergeEdgeStep<>(traversal, isStart,
+                mergeTraversal instanceof GValueConstantTraversal ?
+                        ((GValueConstantTraversal) mergeTraversal).getConstantTraversal() : mergeTraversal);
+        step.setOnCreate(onCreateTraversal instanceof GValueConstantTraversal ?
+                ((GValueConstantTraversal) onCreateTraversal).getConstantTraversal() : onCreateTraversal);
+        step.setOnMatch(onMatchTraversal instanceof GValueConstantTraversal ?
+                ((GValueConstantTraversal) onMatchTraversal).getConstantTraversal() : onMatchTraversal);
+
+        for (final Map.Entry<Object, List<Object>> entry : properties.entrySet()) {
+            for (final Object value : entry.getValue()) {
+                step.addProperty(entry.getKey(), value);
+            }
+        }
+
+        if (inVTraversal != null) {
+            step.addChildOption(Merge.inV, inVTraversal);
+        }
+        if (outVTraversal != null) {
+            step.addChildOption(Merge.outV, outVTraversal);
+        }
+
+        TraversalHelper.copyLabels(this, step, false);
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return onMatchTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal) onMatchTraversal).isParameterized() ||
+                onCreateTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal) onCreateTraversal).isParameterized() ||
+                mergeTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal) mergeTraversal).isParameterized();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (mergeTraversal != null && mergeTraversal instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).updateVariable(name, value);
+        }
+        if (onMatchTraversal != null && onMatchTraversal instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).updateVariable(name, value);
+        }
+        if (onCreateTraversal != null && onCreateTraversal instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).updateVariable(name, value);
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Set<GValue<?>> gValues = new HashSet<>();
+        if (mergeTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) mergeTraversal).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<?, ?>) mergeTraversal).getGValue());
+        }
+        if (onMatchTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) onMatchTraversal).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<?, ?>) onMatchTraversal).getGValue());
+        }
+        if (onCreateTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) onCreateTraversal).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<?, ?>) onCreateTraversal).getGValue());
+        }
+        return gValues;
+    }
+
+    @Override
+    public CallbackRegistry<Event> getMutatingCallbackRegistry() {
+        throw new IllegalStateException("Cannot get mutating CallbackRegistry on GValue placeholder step");
+    }
+
+    /**
+     * This implementation should only be used as a mechanism for supporting {@link PartitionStrategy}.
+     */
+    @Override
+    public void addProperty(Object key, Object value) {
+        if (key instanceof GValue) {
+            throw new IllegalArgumentException("GValue cannot be used as a property key");
+        }
+        if (value instanceof GValue) {
+            traversal.getGValueManager().register((GValue<?>) value);
+        }
+        if (properties.containsKey(key)) {
+            throw new IllegalArgumentException("MergeElement.addProperty only support properties with single cardinality");
+        }
+        properties.put(key, Collections.singletonList(value));
+    }
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return GValueHelper.resolveProperties(properties,
+                gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Map<Object, List<Object>> getPropertiesGValueSafe() {
+        return GValueHelper.resolveProperties(properties);
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (properties.containsKey(k)) {
+            properties.remove(k);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeElementStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeElementStep.java
index e27f953f23..daae4816fd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeElementStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeElementStep.java
@@ -19,6 +19,8 @@
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -34,12 +36,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Deleting;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
@@ -48,6 +45,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequire
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -59,7 +57,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
  * Abstract base class for the {@code mergeV/E()} implementations.
  */
 public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
-        implements Writing<Event>, Deleting<Event>, TraversalOptionParent<Merge, S, C> {
+        implements MergeStepContract<S, E, C> {
 
     protected final boolean isStart;
     protected boolean first = true;
@@ -69,7 +67,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
 
     protected CallbackRegistry<Event> callbackRegistry;
 
-    private Parameters parameters = new Parameters();
+    private Map<Object, List<Object>> properties = new HashMap<>(); // Used to support PartitionStrategy
 
     protected boolean usesPartitionStrategy;
 
@@ -82,11 +80,6 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
         validate(mergeMap, false);
     }
 
-    public MergeElementStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> mergeMap) {
-        this(traversal, isStart, new ConstantTraversal<>(mergeMap));
-        validate(mergeMap.get(), false);
-    }
-
     public MergeElementStep(final Traversal.Admin traversal, final boolean isStart,
                             final Traversal.Admin mergeTraversal) {
         super(traversal);
@@ -106,6 +99,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
      * This {@code Map} also will be used as the default data set to be used to create the element if the search is not
      * successful.
      */
+    @Override
     public Traversal.Admin<S, Map> getMergeTraversal() {
         return mergeTraversal;
     }
@@ -114,6 +108,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
      * Gets the traversal that will be used to provide the {@code Map} that will be used to create elements that
      * do not match the search criteria of {@link #getMergeTraversal()}.
      */
+    @Override
     public Traversal.Admin<S, Map> getOnCreateTraversal() {
         return onCreateTraversal;
     }
@@ -122,6 +117,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
      * Gets the traversal that will be used to provide the {@code Map} that will be used to modify elements that
      * match the search criteria of {@link #getMergeTraversal()}.
      */
+    @Override
     public Traversal.Admin<S, Map<String, ?>> getOnMatchTraversal() {
         return onMatchTraversal;
     }
@@ -129,6 +125,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
     /**
      * Determines if this is a start step.
      */
+    @Override
     public boolean isStart() {
         return isStart;
     }
@@ -136,6 +133,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
     /**
      * Determine if this is the first pass through {@link #processNextStart()}.
      */
+    @Override
     public boolean isFirst() {
         return first;
     }
@@ -164,22 +162,6 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
         return children;
     }
 
-    /**
-     * This implementation should only be used as a mechanism for supporting {@link PartitionStrategy}. Using this
-     * with {@link GraphTraversal#with(String,Object)} will have an ill effect of simply acting like a call to
-     * {@link GraphTraversal#property(Object, Object, Object...)}. No mutating steps currently support use of
-     * {@link GraphTraversal#with(String,Object)} so perhaps it's best to not start with that now.
-     */
-    @Override
-    public void configure(final Object... keyValues) {
-        this.parameters.set(this, keyValues);
-    }
-
-    @Override
-    public Parameters getParameters() {
-        return this.parameters;
-    }
-
     public boolean isUsingPartitionStrategy() {
         return usesPartitionStrategy;
     }
@@ -287,7 +269,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
                             op, allowedTokens, k));
                 }
                 if (k == T.label) {
-                    if (!(GValue.instanceOf(v, GType.STRING))) {
+                    if (!(v instanceof String)) {
                         throw new IllegalArgumentException(String.format(
                                 "%s() and option(onCreate) args expect T.label value to be of String - found: %s", op,
                                 v.getClass().getSimpleName()));
@@ -326,14 +308,12 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
      * null Map == empty Map
      */
     protected Map materializeMap(final Traverser.Admin<S> traverser, Traversal.Admin<S, ?> mapTraversal) {
-        final Object o = TraversalUtil.apply(traverser, mapTraversal);
-        Map map = GValue.getFrom(o);
+        Map map = (Map) TraversalUtil.apply(traverser, mapTraversal);
 
-        // PartitionStrategy uses parameters as a mechanism for setting the partition key. trying to be as specific
-        // as possible here wrt parameters usage to avoid misuse
+        // PartitionStrategy uses properties as a mechanism for setting the partition key.
         if (usesPartitionStrategy) {
             map = null == map ? new LinkedHashMap() : map;
-            for (Map.Entry<Object, List<Object>> entry : parameters.getRaw().entrySet()) {
+            for (Map.Entry<Object, List<Object>> entry : properties.entrySet()) {
                 final Object k = entry.getKey();
                 final List<Object> v = entry.getValue();
                 map.put(k, v.get(0));
@@ -369,7 +349,7 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
         return id != null ? graph.traversal().V(id) : graph.traversal().V();
     }
 
-    protected GraphTraversal searchVerticesLabelConstraint(final GraphTraversal t, final String label) {
+    protected GraphTraversal searchVerticesLabelConstraint(GraphTraversal t, final String label) {
         return label != null ? t.hasLabel(label) : t;
     }
 
@@ -387,4 +367,44 @@ public abstract class MergeElementStep<S, E, C> extends FlatMapStep<S, E>
 
     protected abstract Set getAllowedTokens();
 
+    @Override
+    public void setMerge(final Traversal.Admin<?,Map<Object, Object>> mergeTraversal) {
+        this.mergeTraversal = integrateChild(mergeTraversal);
+    }
+
+    @Override
+    public void setOnCreate(final Traversal.Admin<?,Map<Object, Object>> onCreateTraversal) {
+        this.onCreateTraversal = integrateChild(onCreateTraversal);
+    }
+
+    @Override
+    public void setOnMatch(final Traversal.Admin<?,Map<Object, Object>> onMatchTraversal) {
+        this.onMatchTraversal = integrateChild(onMatchTraversal);
+    }
+
+    /**
+     * This implementation should only be used as a mechanism for supporting {@link PartitionStrategy}.
+     */
+    @Override
+    public void addProperty(Object key, Object value) {
+        if (properties.containsKey(key)) {
+            throw new IllegalArgumentException("MergeElement.addProperty only support properties with single cardinality");
+        }
+        properties.put(key, Collections.singletonList(value));
+    }
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return GValueHelper.resolveProperties(properties);
+    }
+
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (properties.containsKey(k)) {
+            properties.remove(k);
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStepContract.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStepContract.java
new file mode 100644
index 0000000000..d0f3f7b420
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStepContract.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Deleting;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PropertyAdding;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface MergeStepContract<S, E, C> extends Writing<Event>, Deleting<Event>, TraversalOptionParent<Merge, S, C>, PropertyAdding {
+
+    Traversal.Admin<S, Map> getMergeTraversal();
+
+    Traversal.Admin<S, Map> getOnCreateTraversal();
+
+    Traversal.Admin<S, Map<String, ?>> getOnMatchTraversal();
+
+    boolean isStart();
+
+    boolean isFirst();
+
+    boolean isUsingPartitionStrategy();
+
+    void reset();
+
+    Set<TraverserRequirement> getRequirements();
+
+    void setMerge(Traversal.Admin<?,Map<Object, Object>> mergeMap);
+
+    void setOnCreate(Traversal.Admin<?,Map<Object, Object>> onCreateMap);
+
+    void setOnMatch(Traversal.Admin<?,Map<Object, Object>> onMatchMap);
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index f96ebd31f5..279af6519d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -30,7 +30,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.T;
@@ -44,7 +43,7 @@ import static java.util.stream.Collectors.toList;
 /**
  * Implementation for the {@code mergeV()} step covering both the start step version and the one used mid-traversal.
  */
-public class MergeVertexStep<S> extends MergeElementStep<S, Vertex, Map> {
+public class MergeVertexStep<S> extends MergeElementStep<S, Vertex, Map<Object, Object>> {
 
     private static final Set allowedTokens = new LinkedHashSet(Arrays.asList(T.id, T.label));
 
@@ -52,20 +51,15 @@ public class MergeVertexStep<S> extends MergeElementStep<S, Vertex, Map> {
         MergeElementStep.validate(map, ignoreTokens, allowedTokens, "mergeV");
     }
 
-
     public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart) {
         super(traversal, isStart);
     }
 
-    public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Map merge) {
-        super(traversal, isStart, merge);
-    }
-
-    public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) {
+    public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> merge) {
         super(traversal, isStart, merge);
     }
-
-    public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) {
+    
+    public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map<Object, Object>> mergeTraversal) {
         super(traversal, isStart, mergeTraversal);
     }
 
@@ -199,5 +193,4 @@ public class MergeVertexStep<S> extends MergeElementStep<S, Vertex, Map> {
 
         return combinedMap;
     }
-
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStepPlaceholder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStepPlaceholder.java
new file mode 100644
index 0000000000..8f84d67a16
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStepPlaceholder.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueHelper;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Implementation for the {@code mergeV()} step covering both the start step version and the one used mid-traversal.
+ */
+public class MergeVertexStepPlaceholder<S> extends AbstractStep<S, Vertex> implements MergeStepContract<S, Vertex, Map>, GValueHolder<S, Vertex> {
+
+    private static final Set allowedTokens = new LinkedHashSet(Arrays.asList(T.id, T.label));
+    private Map<Object, List<Object>> properties = new HashMap<>();
+
+    public static void validateMapInput(final Map map, final boolean ignoreTokens) {
+        MergeElementStep.validate(map, ignoreTokens, allowedTokens, "mergeV");
+    }
+
+    private Traversal.Admin<?,Map<Object,Object>> mergeTraversal;
+    private Traversal.Admin<?,Map<Object,Object>> onCreateTraversal;
+    private Traversal.Admin<?,Map<Object,Object>> onMatchTraversal;
+    private final boolean isStart;
+
+    public MergeVertexStepPlaceholder(final Traversal.Admin traversal, final boolean isStart) {
+        this(traversal, isStart, new IdentityTraversal<>());
+    }
+
+    public MergeVertexStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> merge) {
+        this(traversal, isStart, new ConstantTraversal<>(merge));
+        validateMapInput(merge, false);
+    }
+
+    public MergeVertexStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final GValue<Map<Object, Object>> merge) {
+        this(traversal, isStart, new GValueConstantTraversal<>(merge));
+        validateMapInput(merge.get(), false);
+    }
+
+    public MergeVertexStepPlaceholder(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<?,Map<Object, Object>> mergeTraversal) {
+        super(traversal);
+        this.isStart = isStart;
+        this.mergeTraversal = mergeTraversal;
+        if (mergeTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getGValue());
+        }
+    }
+
+    @Override
+    protected Traverser.Admin<Vertex> processNextStart() throws NoSuchElementException {
+        return null;
+    }
+
+    @Override
+    public MergeVertexStepPlaceholder<S> clone() {
+        return (MergeVertexStepPlaceholder<S>) super.clone();
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return super.getRequirements();
+    }
+
+    @Override
+    public Traversal.Admin getMergeTraversal() {
+        if (mergeTraversal != null && mergeTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getGValue().getName());
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getConstantTraversal();
+        }
+        return mergeTraversal;
+    }
+
+    public Traversal.Admin getMergeTraversalGValueSafe() {
+        if (mergeTraversal != null && mergeTraversal instanceof GValueConstantTraversal) {
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).getConstantTraversal();
+        }
+        return mergeTraversal;
+    }
+
+    @Override
+    public Traversal.Admin getOnCreateTraversal() {
+        if (onCreateTraversal != null && onCreateTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).getGValue().getName());
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).getConstantTraversal();
+        }
+        return onCreateTraversal;
+    }
+
+    public Traversal.Admin getOnCreateTraversalGValueSafe() {
+        if (onCreateTraversal != null && onCreateTraversal instanceof GValueConstantTraversal) {
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).getConstantTraversal();
+        }
+        return onCreateTraversal;
+    }
+
+    @Override
+    public Traversal.Admin getOnMatchTraversal() {
+        if (onMatchTraversal != null && onMatchTraversal instanceof GValueConstantTraversal) {
+            traversal.getGValueManager().pinVariable(((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).getGValue().getName());
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).getConstantTraversal();
+        }
+        return onMatchTraversal;
+    }
+
+    public Traversal.Admin getOnMatchTraversalGValueSafe() {
+        if (onMatchTraversal != null && onMatchTraversal instanceof GValueConstantTraversal) {
+            return ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).getConstantTraversal();
+        }
+        return onMatchTraversal;
+    }
+
+    @Override
+    public boolean isStart() {
+        return isStart;
+    }
+
+    @Override
+    public boolean isFirst() {
+        return true; // Step cannot be iterated so isFirst() is always true
+    }
+
+    @Override
+    public void addChildOption(Merge token, Traversal.Admin traversalOption) {
+        if (token == Merge.onCreate) {
+            setOnCreate(traversalOption);
+        } else if (token == Merge.onMatch) {
+            setOnMatch(traversalOption);
+        } else {
+            throw new UnsupportedOperationException(String.format("Option %s for Merge is not supported", token.name()));
+        }
+    }
+
+    @Override
+    public boolean isUsingPartitionStrategy() {
+        return false;
+    }
+
+    @Override
+    public void setOnMatch(final Traversal.Admin<?,Map<Object, Object>> onMatchMap) {
+        this.onMatchTraversal = onMatchMap;
+        if (onMatchMap instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchMap).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) onMatchMap).getGValue());
+        }
+    }
+
+    @Override
+    public void setOnCreate(final Traversal.Admin<?,Map<Object, Object>> onCreateMap) {
+        this.onCreateTraversal = onCreateMap;
+        if (onCreateMap instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateMap).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) onCreateMap).getGValue());
+        }
+    }
+
+    @Override
+    public void setMerge(Traversal.Admin<?,Map<Object, Object>> mergeMap) {
+        this.mergeTraversal = mergeMap;
+        if (mergeMap instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, Map<Object, Object>>) mergeMap).isParameterized()) {
+            traversal.getGValueManager().register(((GValueConstantTraversal<?, Map<Object, Object>>) mergeMap).getGValue());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        MergeVertexStepPlaceholder<?> that = (MergeVertexStepPlaceholder<?>) o;
+        return isStart == that.isStart && Objects.equals(properties, that.properties) && Objects.equals(mergeTraversal, that.mergeTraversal) && Objects.equals(onCreateTraversal, that.onCreateTraversal) && Objects.equals(onMatchTraversal, that.onMatchTraversal);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), properties, mergeTraversal, onCreateTraversal, onMatchTraversal, isStart);
+    }
+
+    @Override
+    public MergeVertexStep<S> asConcreteStep() {
+        MergeVertexStep<S> step = new MergeVertexStep<>(traversal, isStart,
+                mergeTraversal instanceof GValueConstantTraversal ?
+                        ((GValueConstantTraversal) mergeTraversal).getConstantTraversal() : mergeTraversal);
+        step.setOnCreate(onCreateTraversal instanceof GValueConstantTraversal ?
+                ((GValueConstantTraversal) onCreateTraversal).getConstantTraversal() : onCreateTraversal);
+        step.setOnMatch(onMatchTraversal instanceof GValueConstantTraversal ?
+                ((GValueConstantTraversal) onMatchTraversal).getConstantTraversal() : onMatchTraversal);
+        TraversalHelper.copyLabels(this, step, false);
+
+        for (final Map.Entry<Object, List<Object>> entry : properties.entrySet()) {
+            for (final Object value : entry.getValue()) {
+                step.addProperty(entry.getKey(), value);
+            }
+        }
+
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return onMatchTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal) onMatchTraversal).isParameterized() ||
+                onCreateTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal) onCreateTraversal).isParameterized() ||
+                mergeTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal) mergeTraversal).isParameterized();
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        if (mergeTraversal != null && mergeTraversal instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<?, Map<Object, Object>>) mergeTraversal).updateVariable(name, value);
+        }
+        if (onMatchTraversal != null && onMatchTraversal instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<?, Map<Object, Object>>) onMatchTraversal).updateVariable(name, value);
+        }
+        if (onCreateTraversal != null && onCreateTraversal instanceof GValueConstantTraversal) {
+            ((GValueConstantTraversal<?, Map<Object, Object>>) onCreateTraversal).updateVariable(name, value);
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        Set<GValue<?>> gValues = new HashSet<>();
+        if (mergeTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) mergeTraversal).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<?, ?>) mergeTraversal).getGValue());
+        }
+        if (onMatchTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) onMatchTraversal).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<?, ?>) onMatchTraversal).getGValue());
+        }
+        if (onCreateTraversal instanceof GValueConstantTraversal && ((GValueConstantTraversal<?, ?>) onCreateTraversal).getGValue().isVariable()) {
+            gValues.add(((GValueConstantTraversal<?, ?>) onCreateTraversal).getGValue());
+        }
+        return gValues;
+    }
+
+    @Override
+    public CallbackRegistry<Event> getMutatingCallbackRegistry() {
+        throw new IllegalStateException("Cannot get mutating CallbackRegistry on GValue placeholder step");
+    }
+
+    /**
+     * This implementation should only be used as a mechanism for supporting {@link PartitionStrategy}.
+     */
+    @Override
+    public void addProperty(Object key, Object value) {
+        if (key instanceof GValue) {
+            throw new IllegalArgumentException("GValue cannot be used as a property key");
+        }
+        if (value instanceof GValue) {
+            traversal.getGValueManager().register((GValue<?>) value);
+        }
+        if (properties.containsKey(key)) {
+            throw new IllegalArgumentException("MergeElement.addProperty only support properties with single cardinality");
+        }
+        properties.put(key, Collections.singletonList(value));
+    }
+
+    @Override
+    public Map<Object, List<Object>> getProperties() {
+        return GValueHelper.resolveProperties(properties,
+                gValue -> traversal.getGValueManager().pinVariable(gValue.getName()));
+    }
+
+    public Map<Object, List<Object>> getPropertiesGValueSafe() {
+        return GValueHelper.resolveProperties(properties);
+    }
+
+    @Override
+    public boolean removeProperty(Object k) {
+        if (properties.containsKey(k)) {
+            properties.remove(k);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/RangeLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/RangeLocalStep.java
index 0907398efc..fa2a9c46f2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/RangeLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/RangeLocalStep.java
@@ -21,7 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeLocalStepContract;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -38,37 +38,34 @@ import java.util.Set;
 /**
  * @author Daniel Kuppitz (http://gremlin.guru)
  */
-public final class RangeLocalStep<S> extends ScalarMapStep<S, S> {
+public final class RangeLocalStep<S> extends ScalarMapStep<S, S> implements RangeLocalStepContract<S> {
 
-    private final GValue<Long> low;
-    private final GValue<Long> high;
+    private long low;
+    private long high;
 
     public RangeLocalStep(final Traversal.Admin traversal, final long low, final long high) {
-        this(traversal, GValue.ofLong(null, low), GValue.ofLong(null, high));
-    }
-
-    public RangeLocalStep(final Traversal.Admin traversal, final GValue<Long> low, final GValue<Long> high) {
         super(traversal);
-        if (low.get() != -1 && high.get() != -1 && low.get() > high.get()) {
+        if (low != -1 && high != -1 && low > high) {
             throw new IllegalArgumentException("Not a legal range: [" + low + ", " + high + ']');
         }
         this.low = low;
         this.high = high;
     }
 
-    public long getLowRange() {
-        return this.low.get();
+    @Override
+    public Long getLowRange() {
+        return this.low;
+    }
+
+    @Override
+    public Long getHighRange() {
+        return this.high;
     }
- 
-    public long getHighRange() {
-        return this.high.get();
-    } 
- 
 
     @Override
     protected S map(final Traverser.Admin<S> traverser) {
         final S start = traverser.get();
-        return applyRange(start, this.low.get(), this.high.get());
+        return applyRange(start, this.low, this.high);
     }
 
     /**
@@ -135,12 +132,12 @@ public final class RangeLocalStep<S> extends ScalarMapStep<S, S> {
 
     @Override
     public String toString() {
-        return StringFactory.stepString(this, this.low.get(), this.high.get());
+        return StringFactory.stepString(this, this.low, this.high);
     }
 
     @Override
     public int hashCode() {
-        return super.hashCode() ^ Long.hashCode(this.high.get()) ^ Long.hashCode(this.low.get());
+        return super.hashCode() ^ Long.hashCode(this.low) ^ Long.hashCode(this.high);
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStep.java
index 2dbe084db8..472304720f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStep.java
@@ -22,7 +22,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TailLocalStepContract;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -35,15 +35,11 @@ import java.util.Set;
 /**
  * @author Matt Frantz (http://github.com/mhfrantz)
  */
-public final class TailLocalStep<S> extends ScalarMapStep<S, S> {
+public final class TailLocalStep<S> extends ScalarMapStep<S, S> implements TailLocalStepContract<S> {
 
-    private final GValue<Long> limit;
+    private final long limit;
 
     public TailLocalStep(final Traversal.Admin traversal, final long limit) {
-        this(traversal, GValue.ofLong(null, limit));
-    }
-
-    public TailLocalStep(final Traversal.Admin traversal, final GValue<Long> limit) {
         super(traversal);
         this.limit = limit;
     }
@@ -60,23 +56,27 @@ public final class TailLocalStep<S> extends ScalarMapStep<S, S> {
                                 start instanceof Path ? ((Path) start).size() :
                                         start instanceof Iterable ? IteratorUtils.count((Iterable) start) :
                                                 IteratorUtils.count(IteratorUtils.asIterator(start));
-        final long low = high - this.limit.get();
+        final long low = high - this.limit;
         final S result = RangeLocalStep.applyRange(start, low, high);
         return result;
     }
 
     @Override
     public String toString() {
-        return StringFactory.stepString(this, this.limit.get());
+        return StringFactory.stepString(this, this.limit);
     }
 
     @Override
     public int hashCode() {
-        return super.hashCode() ^ Long.hashCode(this.limit.get());
+        return super.hashCode() ^ Long.hashCode(this.limit);
     }
 
     @Override
     public Set<TraverserRequirement> getRequirements() {
         return Collections.singleton(TraverserRequirement.OBJECT);
     }
+
+    public Long getLimit() {
+        return limit;
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
index b6230e0538..f4d2743f24 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
@@ -21,7 +21,6 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -45,26 +44,18 @@ import java.util.stream.Collectors;
  *
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implements AutoCloseable, Configuring {
+public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implements AutoCloseable, Configuring, VertexStepContract<E> {
 
     protected Parameters parameters = new Parameters();
     private final String[] edgeLabels;
-    private final GValue<String>[] edgeLabelsGValue;
     private Direction direction;
     private final Class<E> returnClass;
 
     public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final String... edgeLabels) {
-        this(traversal, returnClass, direction, GValue.ensureGValues(edgeLabels));
-    }
-
-    public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final GValue<String>... edgeLabels) {
         super(traversal);
         this.direction = direction;
+        this.edgeLabels = edgeLabels;
         this.returnClass = returnClass;
-        this.edgeLabelsGValue = edgeLabels;
-
-        // convert the GValue<String> to a String[] for the edgeLabels field to cache the values
-        this.edgeLabels = Arrays.stream(this.edgeLabelsGValue).map(GValue::get).toArray(String[]::new);
     }
 
     @Override
@@ -79,30 +70,27 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
 
     @Override
     protected Iterator<E> flatMap(final Traverser.Admin<Vertex> traverser) {
-        // not passing GValue to graphs at this point. if a graph wants to support GValue, it should implement
-        // its own step to do so. in this way, we keep things backwards compatible and don't force folks to have
-        // deal with this until they are ready.
         return Vertex.class.isAssignableFrom(this.returnClass) ?
                 (Iterator<E>) traverser.get().vertices(this.direction, this.edgeLabels) :
                 (Iterator<E>) traverser.get().edges(this.direction, this.edgeLabels);
     }
 
+    @Override
     public Direction getDirection() {
         return this.direction;
     }
 
+    @Override
     public String[] getEdgeLabels() {
         return this.edgeLabels;
     }
 
-    public GValue<String>[] getEdgeLabelsGValue() {
-        return this.edgeLabelsGValue;
-    }
-
+    @Override
     public Class<E> getReturnClass() {
         return this.returnClass;
     }
 
+    @Override
     public void reverseDirection() {
         this.direction = this.direction.opposite();
     }
@@ -110,6 +98,7 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
     /**
      * Determines if the step returns vertices.
      */
+    @Override
     public boolean returnsVertex() {
         return this.returnClass.equals(Vertex.class);
     }
@@ -117,6 +106,7 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
     /**
      * Determines if the step returns edges.
      */
+    @Override
     public boolean returnsEdge() {
         return this.returnClass.equals(Edge.class);
     }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStepContract.java
similarity index 57%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStepContract.java
index d44bde2ce0..729b1f68ed 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TailLocalStepTest.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStepContract.java
@@ -18,24 +18,26 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Scope;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
 
-import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
 
-/**
- * @author Daniel Kuppitz (http://gremlin.guru)
- */
-public class TailLocalStepTest extends StepTest {
-
-    @Override
-    protected List<Traversal> getTraversals() {
-        return Arrays.asList(
-                __.tail(Scope.local, 5L),
-                __.tail(Scope.local, 10L)
-        );
-    }
+public interface VertexStepContract<E extends Element> extends Step<Vertex, E> {
+    Direction getDirection();
+
+    String[] getEdgeLabels();
+
+    Class<E> getReturnClass();
+
+    void reverseDirection();
+
+    boolean returnsVertex();
+
+    boolean returnsEdge();
+
+    Set<TraverserRequirement> getRequirements();
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStepPlaceholder.java
similarity index 50%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStepPlaceholder.java
index b6230e0538..a4147e739f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStepPlaceholder.java
@@ -20,10 +20,11 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
@@ -31,10 +32,11 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -42,67 +44,46 @@ import java.util.stream.Collectors;
 /**
  * Handles the logic of traversing to adjacent vertices or edges given a direction and edge labels for steps like,
  * {@code out}, {@code in}, {@code both}, {@code outE}, {@code inE}, and {@code bothE}.
- *
- * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implements AutoCloseable, Configuring {
+public class VertexStepPlaceholder<E extends Element> extends AbstractStep<Vertex, E> implements GValueHolder<Vertex, E>, VertexStepContract<E> {
 
-    protected Parameters parameters = new Parameters();
-    private final String[] edgeLabels;
-    private final GValue<String>[] edgeLabelsGValue;
+    private final GValue<String>[] edgeLabels;
     private Direction direction;
     private final Class<E> returnClass;
 
-    public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final String... edgeLabels) {
-        this(traversal, returnClass, direction, GValue.ensureGValues(edgeLabels));
-    }
-
-    public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final GValue<String>... edgeLabels) {
+    public VertexStepPlaceholder(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final GValue<String>... edgeLabels) {
         super(traversal);
         this.direction = direction;
+        this.edgeLabels = edgeLabels;
         this.returnClass = returnClass;
-        this.edgeLabelsGValue = edgeLabels;
-
-        // convert the GValue<String> to a String[] for the edgeLabels field to cache the values
-        this.edgeLabels = Arrays.stream(this.edgeLabelsGValue).map(GValue::get).toArray(String[]::new);
-    }
-
-    @Override
-    public Parameters getParameters() {
-        return this.parameters;
-    }
-
-    @Override
-    public void configure(final Object... keyValues) {
-        this.parameters.set(null, keyValues);
+        for (GValue<String> label : edgeLabels) {
+            if (label.isVariable()) {
+                traversal.getGValueManager().register(label);
+            }
+        }
     }
 
     @Override
-    protected Iterator<E> flatMap(final Traverser.Admin<Vertex> traverser) {
-        // not passing GValue to graphs at this point. if a graph wants to support GValue, it should implement
-        // its own step to do so. in this way, we keep things backwards compatible and don't force folks to have
-        // deal with this until they are ready.
-        return Vertex.class.isAssignableFrom(this.returnClass) ?
-                (Iterator<E>) traverser.get().vertices(this.direction, this.edgeLabels) :
-                (Iterator<E>) traverser.get().edges(this.direction, this.edgeLabels);
-    }
-
     public Direction getDirection() {
         return this.direction;
     }
 
+    @Override
     public String[] getEdgeLabels() {
-        return this.edgeLabels;
+        traversal.getGValueManager().pinGValues(Arrays.asList(edgeLabels));
+        return Arrays.stream(GValue.resolveToValues(edgeLabels)).toArray(String[]::new);
     }
 
-    public GValue<String>[] getEdgeLabelsGValue() {
-        return this.edgeLabelsGValue;
+    public String[] getEdgeLabelsGValueSafe() {
+        return Arrays.stream(GValue.resolveToValues(edgeLabels)).toArray(String[]::new);
     }
 
+    @Override
     public Class<E> getReturnClass() {
         return this.returnClass;
     }
 
+    @Override
     public void reverseDirection() {
         this.direction = this.direction.opposite();
     }
@@ -110,6 +91,7 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
     /**
      * Determines if the step returns vertices.
      */
+    @Override
     public boolean returnsVertex() {
         return this.returnClass.equals(Vertex.class);
     }
@@ -117,10 +99,16 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
     /**
      * Determines if the step returns edges.
      */
+    @Override
     public boolean returnsEdge() {
         return this.returnClass.equals(Edge.class);
     }
 
+    @Override
+    protected Traverser.Admin<E> processNextStart() throws NoSuchElementException {
+        throw new IllegalStateException("VertexStepPlaceholder is not executable");
+    }
+
     @Override
     public String toString() {
         return StringFactory.stepString(this, this.direction, Arrays.asList(this.edgeLabels), this.returnClass.getSimpleName().toLowerCase());
@@ -131,9 +119,27 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
         int result = Objects.hash(super.hashCode(), direction, returnClass);
         // edgeLabel's order does not matter because in("x", "y") and in("y", "x") must be considered equal.
         if (edgeLabels != null && edgeLabels.length > 0) {
-            final List<String> sortedEdgeLabels = Arrays.stream(edgeLabels)
-                    .sorted(Comparator.nullsLast(Comparator.naturalOrder())).collect(Collectors.toList());
-            for (final String edgeLabel : sortedEdgeLabels) {
+            final List<GValue<String>> sortedEdgeLabels = Arrays.stream(edgeLabels)
+                    .sorted(Comparator.nullsLast(new Comparator<GValue<String>>() {
+                        @Override
+                        public int compare(GValue<String> o1, GValue<String> o2) {
+                            // variables come before non-variables
+                            if (o1.isVariable() && !o2.isVariable()) {
+                                return -1;
+                            } else if (!o1.isVariable() && o2.isVariable()) {
+                                return 1;
+                            } else if (o1.isVariable()) {
+                                if (!o2.getName().equals(o1.getName())) {
+                                    return o1.getName().compareTo(o2.getName()); // compare by variable name
+                                } else {
+                                    return o1.get().compareTo(o2.get()); // use value as tie-breaker
+                                }
+                            } else {
+                                return o1.get().compareTo(o2.get()); // comparison of 2 non-variables
+                            }
+                        }
+                    })).collect(Collectors.toList());
+            for (final GValue<String> edgeLabel : sortedEdgeLabels) {
                 result = 31 * result + Objects.hashCode(edgeLabel);
             }
         }
@@ -146,7 +152,30 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
     }
 
     @Override
-    public void close() throws Exception {
-        closeIterator();
+    public VertexStep<E> asConcreteStep() {
+        VertexStep<E> step = new VertexStep<>(traversal, returnClass, direction, Arrays.stream(GValue.resolveToValues(edgeLabels))
+                .map(String.class::cast)
+                .toArray(String[]::new));
+        TraversalHelper.copyLabels(this, step, false);
+        return step;
+    }
+
+    @Override
+    public boolean isParameterized() {
+        return Arrays.stream(edgeLabels).anyMatch(gv -> gv.isVariable());
+    }
+
+    @Override
+    public void updateVariable(String name, Object value) {
+        for (int i = 0; i < edgeLabels.length; i++) {
+            if (name.equals(edgeLabels[i].getName())) {
+                edgeLabels[i] = GValue.ofString(name, (String) value);
+            }
+        }
+    }
+
+    @Override
+    public Collection<GValue<?>> getGValues() {
+        return Arrays.asList(edgeLabels);
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index 5f869b6df3..feb02b98d8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -20,9 +20,8 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Deleting;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.*;
@@ -38,9 +37,9 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.structure.util.keyed.KeyedProperty;
 import org.apache.tinkerpop.gremlin.structure.util.keyed.KeyedVertexProperty;
 
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -49,7 +48,7 @@ import java.util.Set;
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
 public class AddPropertyStep<S extends Element> extends SideEffectStep<S>
-        implements Writing<Event.ElementPropertyChangedEvent>, Deleting<Event.ElementPropertyChangedEvent>, TraversalParent, Scoping {
+        implements AddPropertyStepContract<S>, Writing<Event.ElementPropertyChangedEvent>, Deleting<Event.ElementPropertyChangedEvent>, Configuring {
 
     private Parameters parameters = new Parameters();
     private final VertexProperty.Cardinality cardinality;
@@ -71,14 +70,6 @@ public class AddPropertyStep<S extends Element> extends SideEffectStep<S>
         return this.parameters.getReferencedLabels();
     }
 
-    @Override
-    public HashSet<PopInstruction> getPopInstructions() {
-        final HashSet<PopInstruction> popInstructions = new HashSet<>();
-        popInstructions.addAll(Scoping.super.getPopInstructions());
-        popInstructions.addAll(TraversalParent.super.getPopInstructions());
-        return popInstructions;
-    }
-
     @Override
     public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
         return this.parameters.getTraversals();
@@ -204,10 +195,23 @@ public class AddPropertyStep<S extends Element> extends SideEffectStep<S>
         this.parameters.getTraversals().forEach(this::integrateChild);
     }
 
+    @Override
     public VertexProperty.Cardinality getCardinality() {
         return cardinality;
     }
 
+    @Override
+    public Object getKey() {
+        List<Object> keyParams = parameters.get(T.key, null);
+        return keyParams.isEmpty() ? null : keyParams.get(0);
+    }
+
+    @Override
+    public Object getValue() {
+        List<Object> values = parameters.get(T.value, null);
+        return values.isEmpty() ? null : values.get(0);
... 15058 lines suppressed ...