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

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


The following commit(s) were added to refs/heads/master by this push:
     new cd66436470 update concat() to accept traversal varargs and not treat 
inject as special case in argument (#2306)
cd66436470 is described below

commit cd664364706e0c18f561a62b133cc04c03a7ea9c
Author: Yang Xia <55853655+xia...@users.noreply.github.com>
AuthorDate: Mon Oct 23 16:09:48 2023 -0700

    update concat() to accept traversal varargs and not treat inject as special 
case in argument (#2306)
---
 CHANGELOG.asciidoc                                 |  3 +
 docs/src/dev/provider/gremlin-semantics.asciidoc   | 12 ++--
 docs/src/reference/the-traversal.asciidoc          | 37 ++++++-----
 docs/src/upgrade/release-3.7.x.asciidoc            | 31 ++++++++-
 .../grammar/DefaultGremlinBaseVisitor.java         |  2 +-
 .../language/grammar/TraversalMethodVisitor.java   |  8 ++-
 .../traversal/dsl/graph/GraphTraversal.java        | 15 +++--
 .../gremlin/process/traversal/dsl/graph/__.java    |  6 +-
 .../process/traversal/step/map/ConcatStep.java     | 73 ++++++++++++++--------
 .../traversal/translator/DotNetTranslator.java     |  1 -
 .../grammar/TraversalMethodVisitorTest.java        | 28 +++++++++
 .../process/traversal/step/map/ConcatStepTest.java | 10 ++-
 .../Process/Traversal/GraphTraversal.cs            | 20 ++++--
 .../src/Gremlin.Net/Process/Traversal/__.cs        | 10 +--
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 31 ++++-----
 gremlin-go/driver/cucumber/gremlin.go              |  7 ++-
 .../gremlin-javascript/test/cucumber/gremlin.js    |  7 ++-
 gremlin-language/src/main/antlr4/Gremlin.g4        |  2 +-
 gremlin-python/src/main/python/radish/gremlin.py   |  7 ++-
 .../gremlin/test/features/map/Concat.feature       | 41 ++++++++----
 20 files changed, 242 insertions(+), 109 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index be19dd4cde..e3d6bc9e18 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -29,6 +29,9 @@ This release also includes changes from <<release-3-6-5, 
3.6.6>> and <<release-3
 * Added Gherkin parsing support for specific string results using `str[]`.
 * Added the `trim()`, `lTrim()`, `rTrim()`, and `reverse()` steps to perform 
`String` manipulations.
 * Added `replace()`, `split()` and `substring()` steps to perform `String` 
manipulations.
+* Update `concat()` to accept `Traversal` varargs.
+* Corrected `concat()` signatures in `gremlin-dotnet`, `Concat()` is now used 
instead of `Concat<object>()`. *(breaking)*
+* Update `concat()` to not special treat `inject` in arguments and use 
`TraversalUtil.apply` on it as with any other child traversals. *(breaking)*
 * Checked graph features for meta-property support before trying to serialize 
them in `VertexPropertySerializer` for GraphBinary.
 * Added date manipulation steps `asDate`, `dateAdd` and `dateDiff`.
 * Added new data type `DT` to represent periods of time.
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc 
b/docs/src/dev/provider/gremlin-semantics.asciidoc
index c1825c261d..6cf71fda1e 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -730,7 +730,7 @@ 
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#call-step[reference]
 
 *Description:* Concatenates the incoming String traverser with the input 
String arguments, and return the joined String.
 
-*Syntax:* `concat()` | `concat(String...)` | `concat(Traversal)`
+*Syntax:* `concat()` | `concat(String...)` | `concat(Traversal, Traversal...)`
 
 [width="100%",options="header"]
 |=========================================================
@@ -740,10 +740,12 @@ 
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#call-step[reference]
 
 *Arguments:*
 
-* `String...` - If one or more String values are provided, they will be 
concatenated together with the
-incoming traverser. If no argument is provided, the String value from the 
incoming traverser is returned.
-* `Traversal` - The `Traversal` value must resolve to a `String`. The first 
result returned from the traversal will be
-concatenated with the incoming traverser.
+* `concatStrings` - Varargs of `String`. If one or more String values are 
provided, they will be concatenated together
+with the incoming traverser. If no argument is provided, the String value from 
the incoming traverser is returned.
+* `concatTraveral` - A `Traversal` whose must value resolve to a `String`. The 
first result returned from the traversal will
+be concatenated with the incoming traverser.
+* `otherConcatTraverals` - Varargs of `Traversal`. Each `Traversal` value must 
resolve to a `String`. The first result
+returned from each traversal will be concatenated with the incoming traverser 
and the previous traversal arguments.
 
 Any `null` String values will be skipped when concatenated with non-`null` 
String values. If two `null` value are
 concatenated, the `null` value will be propagated and returned.
diff --git a/docs/src/reference/the-traversal.asciidoc 
b/docs/src/reference/the-traversal.asciidoc
index ee9f256d09..9278fef9be 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1143,39 +1143,48 @@ 
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre
 [[concat-step]]
 === Concat Step
 
-The `concat()`-step (*map*) concatenates one or more String values together to 
the incoming String traverser.
+The `concat()`-step (*map*) concatenates one or more String values together to 
the incoming String traverser. This step
+can take either String varargs or Traversal varargs.
+Any `null` String values will be skipped when concatenated with non-`null` 
String values. If two `null` value are
+concatenated, the `null` value will be propagated and returned.
 If the incoming traverser is a non-String value then an 
`IllegalArgumentException` will be thrown.
 
 [gremlin-groovy,modern]
 ----
-g.addV(constant("prefix_").concat(__.V(1).label())).property(id, 10) <1>
+g.addV(constant('prefix_').concat(__.V(1).label())).property(id, 10) <1>
 g.V(10).label()
 g.V().hasLabel('person').values('name').as('a').
     constant('Mr.').concat(__.select('a')) <2>
 g.V().hasLabel('software').as('a').values('name').
     concat(' uses ').
     concat(select('a').values('lang')) <3>
-g.V(1).outE().as("a").constant("").
-    concat(V(1).values("name")).
-    concat(" ").
-    concat(select("a").label()).
-    concat(" ").
-    concat(select("a").inV().values("name")) <4>
-g.inject('hello', 'hi').concat(__.V().values('name')) <5>
-g.inject('This').concat(' ').concat('is a ', 'gremlin.') <6>
+g.V(1).outE().as('a').V(1).values('name').
+    concat(' ').
+    concat(select('a').label()).
+    concat(' ').
+    concat(select("a").inV().values('name')) <4>
+g.V(1).outE().as('a').V(1).values('name').
+    concat(constant(' '),
+        select("a").label(),
+        constant(' '),
+        select('a').inV().values('name')) <5>
+g.inject('hello', 'hi').concat(__.V().values('name')) <6>
+g.inject('This').concat(' ').concat('is a ', 'gremlin.') <7>
 ----
 
 <1> Add a new vertex with id 10 which should be labeled like an existing 
vertex but with some prefix attached
 <2> Attach the prefix "Mr." to all the names using the `constant()`-step
 <3> Generate a string of software names and the language they use
 <4> Generate a string description for each of marko's outgoing edges
-<5> The `concat()` step will append the first result from the child traversal 
to the incoming traverser
-<6> A generic use of `concat()` to join strings together
+<5> Alternative way to generate the string description by using traversal 
varargs. Use the `constant()` step to add
+desired strings between arguments.
+<6> The `concat()` step will append the first result from the child traversal 
to the incoming traverser
+<7> A generic use of `concat()` to join strings together
 
 *Additional References*
 
-link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-String++[`concat(String...)`]
-link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-Traversal++[`concat(Traversal)`]
+link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-java.lang.String-++[`concat(String...)`]
+link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-org.apache.tinkerpop.gremlin.process.traversal.Traversal-org.apache.tinkerpop.gremlin.process.traversal.Traversal...-++[`concat(Taversal,
 Traversal...)`]
 
link:++https://tinkerpop.apache.org/docs/x.y.z/dev/provider/#concat-step++[`Semantics`]
 
 [[connectedcomponent-step]]
diff --git a/docs/src/upgrade/release-3.7.x.asciidoc 
b/docs/src/upgrade/release-3.7.x.asciidoc
index 5b50aa07c8..a17158853d 100644
--- a/docs/src/upgrade/release-3.7.x.asciidoc
+++ b/docs/src/upgrade/release-3.7.x.asciidoc
@@ -30,7 +30,36 @@ complete list of all the modifications that are part of this 
release.
 === Upgrading for Users
 
 ==== String Manipulation Steps
-Additional String manipulation steps have been added to this version.
+Modifications to the concat() step as well as additional String manipulation 
steps have been added to this version.
+
+===== Updates to concat():
+Concat has been modified to take traversal varargs instead of a single 
traversal. Users no longer have to chain
+concat() steps together to concatenate multiple traversals:
+[source,text]
+----
+gremlin> g.V(1).outE().as("a").V(1).values("name").concat(select("a").label(), 
select("a").inV().values("name"))
+==>markocreatedlop
+==>markoknowsvadas
+==>markoknowsjosh
+----
+
+A notable breaking change from 3.7.0 is that we have output order of 
`inject()` as a child of `concat()` to be consistent with other parent steps. 
Any 3.7.0 uses of `concat(inject(X))` should change to `concat(constant(X))` to 
retain the old semantics.
+[source,text]
+----
+// 3.7.0
+gremlin> g.inject("a").concat(inject("b"))
+==>ab
+
+// 3.7.1
+gremlin> g.inject("a").concat(inject())
+==>aa
+gremlin> g.inject("a").concat(inject("b"))
+==>aa
+gremlin> g.inject("a").concat(constant("b"))
+==>ab
+----
+
+link:https://tinkerpop.apache.org/docs/x.y.z/reference/#concat-step[concat()-step],
 
 ===== String Steps asString(), length(), toLower(), toUpper():
 
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 eca310f6aa..63aaaedd03 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
@@ -923,7 +923,7 @@ public class DefaultGremlinBaseVisitor<T> extends 
AbstractParseTreeVisitor<T> im
         * {@inheritDoc}
         */
        @Override
-       public T visitTraversalMethod_concat_Traversal(final 
GremlinParser.TraversalMethod_concat_TraversalContext ctx) { 
notImplemented(ctx); return null; }
+       public T visitTraversalMethod_concat_Traversal_Traversal(final 
GremlinParser.TraversalMethod_concat_Traversal_TraversalContext ctx) { 
notImplemented(ctx); return null; }
        /**
         * {@inheritDoc}
         */
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 dc42e84598..68a3b37307 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
@@ -1745,8 +1745,12 @@ public class TraversalMethodVisitor extends 
TraversalRootVisitor<GraphTraversal>
      * {@inheritDoc}
      */
     @Override
-    public GraphTraversal visitTraversalMethod_concat_Traversal(final 
GremlinParser.TraversalMethod_concat_TraversalContext ctx) {
-        return 
graphTraversal.concat(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+    public GraphTraversal 
visitTraversalMethod_concat_Traversal_Traversal(final 
GremlinParser.TraversalMethod_concat_Traversal_TraversalContext ctx) {
+        if (ctx.getChildCount() == 4) {
+            return 
this.graphTraversal.concat(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+        }
+        return 
this.graphTraversal.concat(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()),
+                
antlr.tListVisitor.visitNestedTraversalList(ctx.nestedTraversalList()));
     }
 
     /**
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 a6257a4eba..48cd33a1dc 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
@@ -1432,21 +1432,24 @@ public interface GraphTraversal<S, E> extends 
Traversal<S, E> {
     }
 
     /**
-     * Concatenate strings.
+     * Concatenate values of an arbitrary number of string traversals to the 
incoming traverser.
      *
      * @return the traversal with an appended {@link ConcatStep}.
+     * @param concatTraversal the traversal to concatenate.
+     * @param otherConcatTraversals additional traversals to concatenate.
      * @see <a 
href="http://tinkerpop.apache.org/docs/${project.version}/reference/#concat-step";
 target="_blank">Reference Documentation - Concat Step</a>
-     * @since 3.7.0
+     * @since 3.7.1
      */
-    public default GraphTraversal<S, String> concat(final Traversal<?, String> 
concatTraversal) {
-        this.asAdmin().getBytecode().addStep(Symbols.concat, concatTraversal);
-        return this.asAdmin().addStep(new ConcatStep<>(this.asAdmin(), 
concatTraversal));
+    public default GraphTraversal<S, String> concat(final Traversal<?, String> 
concatTraversal, final Traversal<?, String>... otherConcatTraversals) {
+        this.asAdmin().getBytecode().addStep(Symbols.concat, concatTraversal, 
otherConcatTraversals);
+        return this.asAdmin().addStep(new ConcatStep<>(this.asAdmin(), 
concatTraversal, otherConcatTraversals));
     }
 
     /**
-     * Concatenate strings.
+     * Concatenate an arbitrary number of strings to the incoming traverser.
      *
      * @return the traversal with an appended {@link ConcatStep}.
+     * @param concatStrings the String values to concatenate.
      * @see <a 
href="http://tinkerpop.apache.org/docs/${project.version}/reference/#concat-step";
 target="_blank">Reference Documentation - Concat Step</a>
      * @since 3.7.0
      */
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 cee80f1dad..f9596b96f0 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
@@ -588,10 +588,10 @@ public class __ {
     }
 
     /**
-     * @see GraphTraversal#concat(Traversal)
+     * @see GraphTraversal#concat(Traversal, Traversal...)
      */
-    public static <A> GraphTraversal<A, String> concat(final Traversal<?, 
String> concatTraversal) {
-        return __.<A>start().concat(concatTraversal);
+    public static <A> GraphTraversal<A, String> concat(final Traversal<A, 
String> concatTraversal, final Traversal<A, String>... otherConcatTraversals) {
+        return __.<A>start().concat(concatTraversal, otherConcatTraversals);
     }
 
     /**
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java
index 982b5969eb..f473561d92 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java
@@ -21,14 +21,17 @@ 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.TraversalParent;
-import 
org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Reference implementation for String concatenation step, a mid-traversal 
step which concatenates one or more
@@ -39,10 +42,10 @@ import java.util.Set;
  */
 public final class ConcatStep<S> extends ScalarMapStep<S, String> implements 
TraversalParent {
 
+    private List<Traversal.Admin<S, String>> concatTraversals;
+    private List<String> concatStrings;
     private String stringArgsResult;
 
-    private Traversal.Admin<S, String> concatTraversal;
-
     // flag used to propagate the null value through if all strings to be 
concatenated are null
     private boolean isNullTraverser = true;
     private boolean isNullTraversal = true;
@@ -50,12 +53,15 @@ public final class ConcatStep<S> extends ScalarMapStep<S, 
String> implements Tra
 
     public ConcatStep(final Traversal.Admin traversal, final String... 
concatStrings) {
         super(traversal);
+        this.concatStrings = 
Collections.unmodifiableList(Arrays.asList(concatStrings));
         this.stringArgsResult = processStrings(concatStrings);
     }
 
-    public ConcatStep(final Traversal.Admin traversal, final Traversal<S, 
String> concatTraversal) {
+    public ConcatStep(final Traversal.Admin traversal, final Traversal<?, 
String> concatTraversal, final Traversal<?, String>... otherConcatTraversals) {
         super(traversal);
-        this.concatTraversal = this.integrateChild(concatTraversal.asAdmin());
+        this.concatTraversals = new 
ArrayList<>(Collections.singletonList((Traversal.Admin<S, String>) 
concatTraversal.asAdmin()));
+        this.concatTraversals.addAll(Stream.of(otherConcatTraversals).map(ct 
-> (Traversal.Admin<S, String>) ct.asAdmin()).collect(Collectors.toList()));
+        this.concatTraversals.forEach(this::integrateChild);
     }
 
     @Override
@@ -74,18 +80,14 @@ public final class ConcatStep<S> extends ScalarMapStep<S, 
String> implements Tra
             sb.append(traverser.get());
         }
 
-        if (null != this.concatTraversal) {
-            if (this.concatTraversal.getStartStep() instanceof InjectStep) { 
// inject as start step is processed separately
-                if (this.concatTraversal.hasNext()) {
-                    final String result = this.concatTraversal.next();
-                    if (null != result) {
-                        this.isNullTraversal = false;
-                        sb.append(result);
-                    }
+        if (null != this.concatTraversals) {
+            this.concatTraversals.forEach(ct -> {
+                final String result = TraversalUtil.apply(traverser, ct);
+                if (null != result) {
+                    this.isNullTraversal = false;
+                    sb.append(result);
                 }
-            } else {
-                sb.append(TraversalUtil.apply(traverser, 
this.concatTraversal));
-            }
+            });
         }
 
         if (!this.isNullString) {
@@ -117,40 +119,59 @@ public final class ConcatStep<S> extends ScalarMapStep<S, 
String> implements Tra
 
     @Override
     public Set<TraverserRequirement> getRequirements() {
-        return Collections.singleton(TraverserRequirement.OBJECT);
+        return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT);
     }
 
     @Override
     public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) {
         super.setTraversal(parentTraversal);
-        this.integrateChild(this.concatTraversal);
+        if (null != this.concatTraversals)
+            for (final Traversal.Admin<S, String> traversal : 
this.concatTraversals) {
+                this.integrateChild(traversal);
+            }
     }
 
     @Override
     public ConcatStep<S> clone() {
         final ConcatStep<S> clone = (ConcatStep<S>) super.clone();
-        if (null != this.concatTraversal)
-            clone.concatTraversal = this.concatTraversal.clone();
+        if (null != this.concatTraversals) {
+            clone.concatTraversals = new ArrayList<>();
+            for (final Traversal.Admin<S, String> concatTraversals : 
this.concatTraversals) {
+                clone.concatTraversals.add(concatTraversals.clone());
+            }
+        }
+        if (null != this.concatStrings) {
+            clone.concatStrings = new ArrayList<>();
+            clone.concatStrings.addAll(this.concatStrings);
+        }
         return clone;
     }
 
     @Override
     public List<Traversal.Admin<S, String>> getLocalChildren() {
-        return null == this.concatTraversal ? Collections.emptyList() : 
Collections.singletonList(this.concatTraversal);
+        return null == this.concatTraversals ? Collections.emptyList() : 
this.concatTraversals;
     }
 
     @Override
     public String toString() {
-        return StringFactory.stepString(this, this.concatTraversal);
+        if (null != this.concatTraversals)
+            return StringFactory.stepString(this, this.concatTraversals);
+        return StringFactory.stepString(this, this.concatStrings);
     }
 
     @Override
     public int hashCode() {
         int result = super.hashCode();
-        if (null != this.concatTraversal)
-            result = super.hashCode() ^ this.concatTraversal.hashCode();
-        if (null != this.stringArgsResult)
-            result = super.hashCode() ^ this.stringArgsResult.hashCode();
+        if (null != this.concatTraversals) {
+            for (final Traversal t : this.concatTraversals) {
+                result = 31 * result + (null != t ? t.hashCode() : 0);
+            }
+        }
+        if (null != this.concatStrings) {
+            for (final String s : this.concatStrings) {
+                result = 31 * result + (null != s ? s.hashCode() : 0);
+            }
+        }
         return result;
     }
 }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
index 017e774c39..5421b45b76 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
@@ -528,7 +528,6 @@ public final class DotNetTranslator implements 
Translator.ScriptTranslator {
             TO_CS_MAP.put(GraphTraversal.Symbols.cap, "Cap<object>");
             TO_CS_MAP.put(GraphTraversal.Symbols.choose, "Choose<object>");
             TO_CS_MAP.put(GraphTraversal.Symbols.coalesce, "Coalesce<object>");
-            TO_CS_MAP.put(GraphTraversal.Symbols.concat, "Concat<object>");
             TO_CS_MAP.put(GraphTraversal.Symbols.constant, "Constant<object>");
             TO_CS_MAP.put(GraphTraversal.Symbols.elementMap, 
"ElementMap<object>");
             TO_CS_MAP.put(GraphTraversal.Symbols.flatMap, "FlatMap<object>");
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
index a4c4ddd33f..17a4d0522c 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
@@ -1153,6 +1153,34 @@ public class TraversalMethodVisitorTest {
                 eval("g.V().coalesce(__.E(1,2),__.addE('person'))"));
     }
 
+    @Test
+    public void shouldParseTraversalMethod_concat_Empty() throws Exception {
+        compare(g.V().concat(), eval("g.V().concat()"));
+    }
+
+    @Test
+    public void shouldParseTraversalMethod_concat_multipleStringArgs() throws 
Exception {
+        compare(g.V().concat("hello", "world"), eval("g.V().concat('hello', 
'world')"));
+    }
+
+    @Test
+    public void shouldParseTraversalMethod_concat_traversal() throws Exception 
{
+        compare(g.V().concat(constant("hello")),
+                eval("g.V().concat(__.constant('hello'))"));
+    }
+
+    @Test
+    public void shouldParseTraversalMethod_concat_multipleTraversalArgs() 
throws Exception {
+        compare(g.V().concat(constant("hello"), constant("world")),
+                eval("g.V().concat(__.constant('hello'), 
__.constant('world'))"));
+    }
+
+    @Test
+    public void shouldParseTraversalMethod_concat_ArgsWithNulls() throws 
Exception {
+        compare(g.V().concat(null, "hello"),
+                eval("g.V().concat(null, 'hello')"));
+    }
+
     @Test
     public void shouldParseTraversalMethod_asString_Empty() throws Exception {
         compare(g.V().asString(), eval("g.V().asString()"));
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java
index 0c17deac69..4229aa6418 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java
@@ -40,20 +40,24 @@ public class ConcatStepTest extends StepTest {
 
     @Test
     public void testReturnTypes() {
+        assertEquals("a", __.__("a").concat().next());
         assertEquals("abc", __.__("a").concat("b", "c").next());
-        assertEquals("ab", __.__("a").concat(__.inject("b", "c")).next());
-        assertEquals("ab", __.__("a").concat(__.inject(Arrays.asList("b", 
"c")).unfold()).next());
+        assertEquals("aa", __.__("a").concat(__.inject("b", "c")).next());
+        assertEquals("aa", __.__("a").concat(__.inject(Arrays.asList("b", 
"c")).unfold()).next());
         assertEquals("", __.__("").concat("").next());
 
         assertArrayEquals(new String[]{"a", "b", "c"},
                 __.__("a", "b", "c").concat().toList().toArray());
         assertArrayEquals(new String[]{"ade", "bde", "cde"},
                 __.__("a", "b", "c").concat("d", "e").toList().toArray());
-        assertArrayEquals(new String[]{"ad", "be", "c"},
+        assertArrayEquals(new String[]{"aa", "bb", "cc"},
                 __.__("a", "b", "c").concat(__.inject("d", 
"e")).toList().toArray());
 
         assertArrayEquals(new String[]{"Mr.a", "Mr.b", "Mr.c", "Mr.d"},
                 __.__("a", "b", "c", 
"d").as("letters").constant("Mr.").concat(__.select("letters")).toList().toArray());
+        assertArrayEquals(new String[]{"Hello Mr.a", "Hello Mr.b", "Hello 
Mr.c", "Hello Mr.d"},
+                __.__("a", "b", "c", "d").as("letters").
+                        constant("Hello ").concat(__.constant("Mr."), 
__.select("letters")).toList().toArray());
 
         String nullStr = null;
         assertNull(__.inject(null).concat(nullStr).next());
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs 
b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index d232bff33d..de7cc131ea 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -541,23 +541,33 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Adds the concat step to this <see cref="GraphTraversal{SType, 
EType}" />.
         /// </summary>
-        public GraphTraversal<TStart, TEnd> Concat<TNewEnd> (ITraversal 
concatTraversal)
+        public GraphTraversal<TStart, string> Concat (ITraversal 
concatTraversal, params ITraversal[]? otherConcatTraversals)
         {
-            Bytecode.AddStep("concat", concatTraversal);
-            return Wrap<TStart, TEnd>(this);
+            List<object?> args;
+            if (otherConcatTraversals == null)
+            {
+                args = new List<object?> { concatTraversal };
+            }
+            else
+            {
+                args = new List<object?>(1 + otherConcatTraversals.Length) { 
concatTraversal };
+                args.AddRange(otherConcatTraversals);
+            }
+            Bytecode.AddStep("concat", args.ToArray());
+            return Wrap<TStart, string>(this);
         }
 
         /// <summary>
         ///     Adds the concat step to this <see cref="GraphTraversal{SType, 
EType}" />.
         /// </summary>
-        public GraphTraversal<TStart, TEnd> Concat<TNewEnd> (params string?[] 
concatStrings)
+        public GraphTraversal<TStart, string> Concat (params string?[] 
concatStrings)
         {
             // need null check?
 
             var args = new List<object?>(concatStrings.Length);
             args.AddRange(concatStrings);
             Bytecode.AddStep("concat", args.ToArray());
-            return Wrap<TStart, TEnd>(this);
+            return Wrap<TStart, string>(this);
         }
 
         /// <summary>
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs 
b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
index 070644c89d..7bda3700e7 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
@@ -347,19 +347,19 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds 
the concat step to that traversal.
         /// </summary>
-        public static GraphTraversal<object, object> Concat<E2> (ITraversal 
concatTraversal)
+        public static GraphTraversal<object, string> Concat (ITraversal 
concatTraversal, ITraversal[]? otherConcatTraversals)
         {
-            return new GraphTraversal<object, 
object>().Concat<E2>(concatTraversal);          
+            return new GraphTraversal<object, 
string>().Concat(concatTraversal, otherConcatTraversals);           
         }
 
         /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds 
the concat step to that traversal.
         /// </summary>
-        public static GraphTraversal<object, object> Concat<E2>(params 
string?[] concatStrings)
+        public static GraphTraversal<object, string> Concat(params string?[] 
concatStrings)
         {
             return concatStrings is { Length: 0 }
-                ? new GraphTraversal<object, object>().Concat<E2>()
-                : new GraphTraversal<object, 
object>().Concat<E2>(concatStrings);          
+                ? new GraphTraversal<object, string>().Concat()
+                : new GraphTraversal<object, string>().Concat(concatStrings);
         }
 
         /// <summary>
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 718d60d0f9..f2cbf513b0 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -537,7 +537,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_E_asString", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.E().AsString()}}, 
                {"g_V_properties", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.V().Properties<object>().AsString()}}, 
                {"g_V_hasLabelXpersonX_valuesXageX_asString", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("age").AsString()}}, 
-               
{"g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().HasLabel("person").Values<object>("age").AsString().Concat<object>(" 
years old")}}, 
+               
{"g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("age").AsString().Concat(" 
years old")}}, 
                {"g_call", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Call<object>()}}, 
                {"g_callXlistX", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Call<object>("--list")}}, 
                {"g_callXlistX_withXstring_stringX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Call<object>("--list").With("service","tinker.search")}}, 
@@ -564,20 +564,21 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                
{"g_V_coalesceXoutXlikesX_outXknowsX_inXcreatedXX_groupCount_byXnameX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Coalesce<object>(__.Out("likes"),__.Out("knows"),__.Out("created")).GroupCount<object>().By("name")}},
 
                
{"g_V_coalesceXoutEXknowsX_outEXcreatedXX_otherV_path_byXnameX_byXlabelX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Coalesce<object>(__.OutE("knows"),__.OutE("created")).OtherV().Path().By("name").By(T.Label)}},
 
                {"g_V_outXcreatedX_order_byXnameX_coalesceXname_constantXxXX", 
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Out("created").Order().By("name").Coalesce<object>(__.Values<object>("name"),__.Constant<object>("x"))}},
 
-               {"g_injectXa_bX_concat", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.Inject("a","b").Concat<object>()}}, 
-               {"g_injectXa_bX_concat_XcX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a","b").Concat<object>("c")}}, 
-               {"g_injectXa_bX_concat_Xc_dX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a","b").Concat<object>("c","d")}}, 
-               {"g_injectXa_bX_concat_Xinject_c_dX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a","b").Concat<object>(__.Inject("c","d"))}}, 
-               {"g_injectXListXa_bXcX_concat_XdX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject(p["xx1"],"c").Concat<object>("d")}}, 
-               {"g_injectXaX_concat_Xinject_List_b_cX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a").Concat<object>(__.Inject(p["xx1"]))}}, 
-               {"g_injectXnullX_concat_XinjectX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>(null).Concat<object>()}}, 
-               {"g_injectXnull_aX_concat_Xnull_bX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>(null,"a").Concat<object>(null,"b")}}, 
-               {"g_injectXhello_hiX_concat_XV_valuesXnameXX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.Inject("hello","hi").Concat<object>(__.V().Order().By(T.Id).Values<object>("name"))}},
 
-               {"g_V_hasLabel_value_concat_X_X_concat_XpersonX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("name").Concat<object>(" 
").Concat<object>("person")}}, 
-               
{"g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().HasLabel("person").Values<object>("name").As("a").Constant<object>("Mr.").Concat<object>(__.Select<object>("a"))}},
 
-               
{"g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().HasLabel("software").As("a").Values<object>("name").Concat<object>(" 
uses ").Concat<object>(__.Select<object>("a").Values<object>("lang"))}}, 
-               
{"g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat<object>(__.Select<object>("a").Label()).Concat<object>(__.Select<object>("a").InV().Values<object>("name"))}},
 
-               {"g_addVXconstantXprefix_X_concatXVX1X_labelX_label", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name","ripple").Proper
 [...]
+               {"g_injectXa_bX_concat", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.Inject("a","b").Concat()}}, 
+               {"g_injectXa_bX_concat_XcX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a","b").Concat("c")}}, 
+               {"g_injectXa_bX_concat_Xc_dX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a","b").Concat("c","d")}}, 
+               {"g_injectXa_bX_concat_Xinject_c_dX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a","b").Concat(__.Inject("c"))}}, 
+               {"g_injectXaX_concat_Xinject_List_b_cX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject("a").Concat(__.Inject(p["xx1"]))}}, 
+               {"g_injectXListXa_bXcX_concat_XdX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject(p["xx1"],"c").Concat("d")}}, 
+               {"g_injectXnullX_concat_XinjectX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>(null).Concat()}}, 
+               {"g_injectXnull_aX_concat_Xnull_bX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>(null,"a").Concat(null,"b")}}, 
+               {"g_injectXhello_hiX_concat_XV_valuesXnameXX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.Inject("hello","hi").Concat(__.V().Order().By(T.Id).Values<object>("name"))}},
 
+               {"g_V_hasLabel_value_concat_X_X_concat_XpersonX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("name").Concat(" 
").Concat("person")}}, 
+               
{"g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().HasLabel("person").Values<object>("name").As("a").Constant<object>("Mr.").Concat(__.Select<object>("a"))}},
 
+               
{"g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").As("a").Values<object>("name").Concat(" 
uses ").Concat(__.Select<object>("a").Values<object>("lang"))}}, 
+               
{"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label()).Concat(__.Select<object>("a").InV().Values<object>("name"))}},
 
+               
{"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label(),__.Select<object>("a").InV().Values<object>("name"))}},
 
+               {"g_addVXconstantXprefix_X_concatXVX1X_labelX_label", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name","ripple").Proper
 [...]
                {"g_V_connectedComponent_hasXcomponentX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}},
 
                {"g_V_dedup_connectedComponent_hasXcomponentX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Dedup().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}},
 
                
{"g_V_hasLabelXsoftwareX_connectedComponent_project_byXnameX_byXcomponentX", 
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().HasLabel("software").ConnectedComponent().Project<object>("name","component").By("name").By("gremlin.connectedComponentVertexProgram.component")}},
 
diff --git a/gremlin-go/driver/cucumber/gremlin.go 
b/gremlin-go/driver/cucumber/gremlin.go
index b78f9629c0..fcc5e3a4c8 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -538,16 +538,17 @@ var translationMap = map[string][]func(g 
*gremlingo.GraphTraversalSource, p map[
     "g_injectXa_bX_concat": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", 
"b").Concat()}}, 
     "g_injectXa_bX_concat_XcX": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", 
"b").Concat("c")}}, 
     "g_injectXa_bX_concat_Xc_dX": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", 
"b").Concat("c", "d")}}, 
-    "g_injectXa_bX_concat_Xinject_c_dX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject("a", 
"b").Concat(gremlingo.T__.Inject("c", "d"))}}, 
-    "g_injectXListXa_bXcX_concat_XdX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject(p["xx1"], "c").Concat("d")}}, 
+    "g_injectXa_bX_concat_Xinject_c_dX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject("a", 
"b").Concat(gremlingo.T__.Inject("c"))}}, 
     "g_injectXaX_concat_Xinject_List_b_cX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.Inject("a").Concat(gremlingo.T__.Inject(p["xx1"]))}}, 
+    "g_injectXListXa_bXcX_concat_XdX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject(p["xx1"], "c").Concat("d")}}, 
     "g_injectXnullX_concat_XinjectX": {func(g *gremlingo.GraphTraversalSource, 
p map[string]interface{}) *gremlingo.GraphTraversal {return 
g.Inject(nil).Concat()}}, 
     "g_injectXnull_aX_concat_Xnull_bX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject(nil, "a").Concat(nil, "b")}}, 
     "g_injectXhello_hiX_concat_XV_valuesXnameXX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject("hello", 
"hi").Concat(gremlingo.T__.V().Order().By(gremlingo.T.Id).Values("name"))}}, 
     "g_V_hasLabel_value_concat_X_X_concat_XpersonX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().HasLabel("person").Values("name").Concat(" ").Concat("person")}}, 
     "g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX": 
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().HasLabel("person").Values("name").As("a").Constant("Mr.").Concat(gremlingo.T__.Select("a"))}},
 
     
"g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX":
 {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().HasLabel("software").As("a").Values("name").Concat(" uses 
").Concat(gremlingo.T__.Select("a").Values("lang"))}}, 
-    
"g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX":
 {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values("name").Concat(gremlingo.T__.Select("a").Label()).Concat(gremlingo.T__.Select("a").InV().Values("name"))}},
 
+    
"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX":
 {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values("name").Concat(gremlingo.T__.Select("a").Label()).Concat(gremlingo.T__.Select("a").InV().Values("name"))}},
 
+    
"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX":
 {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values("name").Concat(gremlingo.T__.Select("a").Label(),
 gremlingo.T__.Select("a").InV().Values("name"))}}, 
     "g_addVXconstantXprefix_X_concatXVX1X_labelX_label": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.AddV("person").Property("name", 
"marko").Property("age", 29).As("marko").AddV("person").Property("name", 
"vadas").Property("age", 27).As("vadas").AddV("software").Property("name", 
"lop").Property("lang", "java").As("lop").AddV("person").Property("name", 
"josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripp 
[...]
     "g_V_connectedComponent_hasXcomponentX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}},
 
     "g_V_dedup_connectedComponent_hasXcomponentX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Dedup().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}},
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 2fff0455c8..3750ae85e4 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -558,16 +558,17 @@ const gremlins = {
     g_injectXa_bX_concat: [function({g}) { return g.inject("a","b").concat() 
}], 
     g_injectXa_bX_concat_XcX: [function({g}) { return 
g.inject("a","b").concat("c") }], 
     g_injectXa_bX_concat_Xc_dX: [function({g}) { return 
g.inject("a","b").concat("c","d") }], 
-    g_injectXa_bX_concat_Xinject_c_dX: [function({g}) { return 
g.inject("a","b").concat(__.inject("c","d")) }], 
-    g_injectXListXa_bXcX_concat_XdX: [function({g, xx1}) { return 
g.inject(xx1,"c").concat("d") }], 
+    g_injectXa_bX_concat_Xinject_c_dX: [function({g}) { return 
g.inject("a","b").concat(__.inject("c")) }], 
     g_injectXaX_concat_Xinject_List_b_cX: [function({g, xx1}) { return 
g.inject("a").concat(__.inject(xx1)) }], 
+    g_injectXListXa_bXcX_concat_XdX: [function({g, xx1}) { return 
g.inject(xx1,"c").concat("d") }], 
     g_injectXnullX_concat_XinjectX: [function({g}) { return 
g.inject(null).concat() }], 
     g_injectXnull_aX_concat_Xnull_bX: [function({g}) { return 
g.inject(null,"a").concat(null,"b") }], 
     g_injectXhello_hiX_concat_XV_valuesXnameXX: [function({g}) { return 
g.inject("hello","hi").concat(__.V().order().by(T.id).values("name")) }], 
     g_V_hasLabel_value_concat_X_X_concat_XpersonX: [function({g}) { return 
g.V().hasLabel("person").values("name").concat(" ").concat("person") }], 
     g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX: 
[function({g}) { return 
g.V().hasLabel("person").values("name").as("a").constant("Mr.").concat(__.select("a"))
 }], 
     
g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX:
 [function({g}) { return 
g.V().hasLabel("software").as("a").values("name").concat(" uses 
").concat(__.select("a").values("lang")) }], 
-    
g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX:
 [function({g, vid1}) { return 
g.V(vid1).outE().as("a").V(vid1).values("name").concat(__.select("a").label()).concat(__.select("a").inV().values("name"))
 }], 
+    
g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX:
 [function({g, vid1}) { return 
g.V(vid1).outE().as("a").V(vid1).values("name").concat(__.select("a").label()).concat(__.select("a").inV().values("name"))
 }], 
+    
g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX:
 [function({g, vid1}) { return 
g.V(vid1).outE().as("a").V(vid1).values("name").concat(__.select("a").label(),__.select("a").inV().values("name"))
 }], 
     g_addVXconstantXprefix_X_concatXVX1X_labelX_label: [function({g, vid1}) { 
return 
g.addV("person").property("name","marko").property("age",29).as("marko").addV("person").property("name","vadas").property("age",27).as("vadas").addV("software").property("name","lop").property("lang","java").as("lop").addV("person").property("name","josh").property("age",32).as("josh").addV("software").property("name","ripple").property("lang","java").as("ripple").addV("person").property("name","peter").
 [...]
     g_V_connectedComponent_hasXcomponentX: [function({g}) { return 
g.V().connectedComponent().has("gremlin.connectedComponentVertexProgram.component")
 }], 
     g_V_dedup_connectedComponent_hasXcomponentX: [function({g}) { return 
g.V().dedup().connectedComponent().has("gremlin.connectedComponentVertexProgram.component")
 }], 
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 
b/gremlin-language/src/main/antlr4/Gremlin.g4
index fe38cd5567..13f4384232 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -830,7 +830,7 @@ traversalMethod_call
     ;
 
 traversalMethod_concat
-    : 'concat' LPAREN nestedTraversal RPAREN #traversalMethod_concat_Traversal
+    : 'concat' LPAREN nestedTraversal (COMMA nestedTraversalList)? RPAREN 
#traversalMethod_concat_Traversal_Traversal
     | 'concat' LPAREN stringLiteralVarargs RPAREN 
#traversalMethod_concat_String
     ;
 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py 
b/gremlin-python/src/main/python/radish/gremlin.py
index 71665597ae..915535ece3 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -540,16 +540,17 @@ world.gremlins = {
     'g_injectXa_bX_concat': [(lambda g:g.inject('a','b').concat())], 
     'g_injectXa_bX_concat_XcX': [(lambda g:g.inject('a','b').concat('c'))], 
     'g_injectXa_bX_concat_Xc_dX': [(lambda 
g:g.inject('a','b').concat('c','d'))], 
-    'g_injectXa_bX_concat_Xinject_c_dX': [(lambda 
g:g.inject('a','b').concat(__.inject('c','d')))], 
-    'g_injectXListXa_bXcX_concat_XdX': [(lambda g, 
xx1=None:g.inject(xx1,'c').concat('d'))], 
+    'g_injectXa_bX_concat_Xinject_c_dX': [(lambda 
g:g.inject('a','b').concat(__.inject('c')))], 
     'g_injectXaX_concat_Xinject_List_b_cX': [(lambda g, 
xx1=None:g.inject('a').concat(__.inject(xx1)))], 
+    'g_injectXListXa_bXcX_concat_XdX': [(lambda g, 
xx1=None:g.inject(xx1,'c').concat('d'))], 
     'g_injectXnullX_concat_XinjectX': [(lambda g:g.inject(None).concat())], 
     'g_injectXnull_aX_concat_Xnull_bX': [(lambda 
g:g.inject(None,'a').concat(None,'b'))], 
     'g_injectXhello_hiX_concat_XV_valuesXnameXX': [(lambda 
g:g.inject('hello','hi').concat(__.V().order().by(T.id_).name))], 
     'g_V_hasLabel_value_concat_X_X_concat_XpersonX': [(lambda 
g:g.V().hasLabel('person').name.concat(' ').concat('person'))], 
     'g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX': 
[(lambda 
g:g.V().hasLabel('person').name.as_('a').constant('Mr.').concat(__.select('a')))],
 
     
'g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX':
 [(lambda g:g.V().hasLabel('software').as_('a').name.concat(' uses 
').concat(__.select('a').lang))], 
-    
'g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX':
 [(lambda g, 
vid1=None:g.V(vid1).outE().as_('a').V(vid1).name.concat(__.select('a').label()).concat(__.select('a').in_v().name))],
 
+    
'g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX':
 [(lambda g, 
vid1=None:g.V(vid1).outE().as_('a').V(vid1).name.concat(__.select('a').label()).concat(__.select('a').in_v().name))],
 
+    
'g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX':
 [(lambda g, 
vid1=None:g.V(vid1).outE().as_('a').V(vid1).name.concat(__.select('a').label(),__.select('a').in_v().name))],
 
     'g_addVXconstantXprefix_X_concatXVX1X_labelX_label': [(lambda g, 
vid1=None:g.addV('person').property('name','marko').property('age',29).as_('marko').addV('person').property('name','vadas').property('age',27).as_('vadas').addV('software').property('name','lop').property('lang','java').as_('lop').addV('person').property('name','josh').property('age',32).as_('josh').addV('software').property('name','ripple').property('lang','java').as_('ripple').addV('person').property('name','peter').p
 [...]
     'g_V_connectedComponent_hasXcomponentX': [(lambda 
g:g.V().connectedComponent().has('gremlin.connectedComponentVertexProgram.component'))],
 
     'g_V_dedup_connectedComponent_hasXcomponentX': [(lambda 
g:g.V().dedup().connectedComponent().has('gremlin.connectedComponentVertexProgram.component'))],
 
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature
index bced98cdad..c9a0e717cc 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature
@@ -62,35 +62,37 @@ Feature: Step - concat()
     Given the empty graph
     And the traversal of
       """
-      g.inject("a", "b").concat(__.inject("c", "d"))
+      g.inject("a", "b").concat(__.inject("c"))
       """
     When iterated to list
     Then the result should be unordered
       | result |
-      | ac |
-      | bd |
+      | aa |
+      | bb |
 
   @GraphComputerVerificationInjectionNotSupported
-  Scenario: g_injectXListXa_bXcX_concat_XdX
+  Scenario: g_injectXaX_concat_Xinject_List_b_cX
     Given the empty graph
-    And using the parameter xx1 defined as "l[a,b]"
+    And using the parameter xx1 defined as "l[b,c]"
     And the traversal of
       """
-      g.inject(xx1,"c").concat("d")
+      g.inject("a").concat(__.inject(xx1))
       """
     When iterated to list
-    Then the traversal will raise an error with message containing text of 
"String concat() can only take string as argument"
+    Then the result should be unordered
+      | result |
+      | aa |
 
   @GraphComputerVerificationInjectionNotSupported
-  Scenario: g_injectXaX_concat_Xinject_List_b_cX
+  Scenario: g_injectXListXa_bXcX_concat_XdX
     Given the empty graph
-    And using the parameter xx1 defined as "l[b,c]"
+    And using the parameter xx1 defined as "l[a,b]"
     And the traversal of
       """
-      g.inject("a").concat(__.inject(xx1))
+      g.inject(xx1,"c").concat("d")
       """
     When iterated to list
-    Then the traversal will raise an error
+    Then the traversal will raise an error with message containing text of 
"String concat() can only take string as argument"
 
   @GraphComputerVerificationInjectionNotSupported
   Scenario: g_injectXnullX_concat_XinjectX
@@ -172,7 +174,7 @@ Feature: Step - concat()
       | ripple uses java |
 
   @GraphComputerVerificationMidVNotSupported
-  Scenario: 
g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX
+  Scenario: 
g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX
     Given the modern graph
     And using the parameter vid1 defined as "v[marko].id"
     And the traversal of
@@ -186,6 +188,21 @@ Feature: Step - concat()
       | markoknowsvadas |
       | markoknowsjosh |
 
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V(vid1).outE().as("a").V(vid1).values("name").concat(select("a").label(), 
select("a").inV().values("name"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | markocreatedlop |
+      | markoknowsvadas |
+      | markoknowsjosh |
+
   Scenario: g_addVXconstantXprefix_X_concatXVX1X_labelX_label
     Given the empty graph
     And the graph initializer of

Reply via email to