You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2021/09/03 17:07:13 UTC

[tinkerpop] 01/02: TINKERPOP-2605 withSideEffect() allows null value

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

spmallette pushed a commit to branch TINKERPOP-2605
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 31ea72ae175e77f6e5d1f16239f048085726b187
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Sep 3 07:19:33 2021 -0400

    TINKERPOP-2605 withSideEffect() allows null value
---
 CHANGELOG.asciidoc                                 |   1 +
 docs/src/upgrade/release-3.5.x.asciidoc            |   1 +
 .../gremlin/process/traversal/Operator.java        |  53 +++-
 .../strategy/decoration/SideEffectStrategy.java    |   2 +-
 .../util/DefaultTraversalSideEffects.java          |   4 -
 .../gremlin/process/traversal/OperatorTest.java    | 344 ++++++++++++++-------
 .../util/DefaultTraversalSideEffectsTest.java      |  26 ++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |   4 +-
 .../gremlin-javascript/test/cucumber/gremlin.js    |   4 +-
 gremlin-python/src/main/python/radish/gremlin.py   |   4 +-
 .../GraphBinaryRemoteGraphComputerProvider.java    |   4 +
 .../GraphSONRemoteGraphComputerProvider.java       |   4 +
 .../remote/GryoRemoteGraphComputerProvider.java    |   4 +
 ...emoteGraphGroovyTranslatorComputerProvider.java |   4 +
 gremlin-test/features/map/Select.feature           |  28 +-
 .../process/traversal/step/map/SelectTest.java     |  18 ++
 16 files changed, 360 insertions(+), 145 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index edaff36..863f836 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -33,6 +33,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Improved handling of `null` when passed to `P` predicates.
 * Handled `null` for mathematical reducing operations of `sum()`, `mean()`, `max()` and `min()`.
 * Allowed `null` values in `Memory` for `GraphComputer`.
+* Allowed `null` assignment in `withSideEffect()`.
 * Fixed problems with `inject(null)` variations where `null` was the only value being submitted.
 * Fixed problem with `GroovyTranslator` and `inject(null,null)` which could be interpreted as the Groovy JDK extension `inject(Object,Closure).
 * Fixed error where certain variants of `inject()` with `null` might not properly construct a traversal in .NET.
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index ac21888..a2e6d0a 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -42,6 +42,7 @@ failures, but now filter results. As these structural elements of the graph can'
 filter all elements.
 * `hasValue(null)` - Formerly this method would throw a general `NullPointerException`, but now it will filter as
 expected.
+* `withSideEffect()` - This configuration step can now take a `null` for its value.
 * `sum()`, `mean()`, `max()` and `min()` - These methods used to throw general `NullPointerException` failures, but
 now ignore `null` values when other numbers are present. If all of the values in the stream are `null` then `hasNext()`
 return `false`.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
index 4fada2f..ea84f2d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
@@ -34,6 +34,7 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * An addition function.
      *
+     * @see NumberHelper#add(Number, Number)
      * @since 3.0.0-incubating
      */
     sum {
@@ -45,6 +46,7 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * A subtraction function.
      *
+     * @see NumberHelper#sub(Number, Number)
      * @since 3.0.0-incubating
      */
     minus {
@@ -56,6 +58,7 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * A multiplication function.
      *
+     * @see NumberHelper#mul(Number, Number)
      * @since 3.0.0-incubating
      */
     mult {
@@ -67,6 +70,7 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * A division function.
      *
+     * @see NumberHelper#div(Number, Number)
      * @since 3.0.0-incubating
      */
     div {
@@ -78,6 +82,7 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * Selects the smaller of the values.
      *
+     * @see NumberHelper#min(Number, Number)
      * @since 3.0.0-incubating
      */
     min {
@@ -89,6 +94,7 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * Selects the larger of the values.
      *
+     * @see NumberHelper#max(Number, Number)
      * @since 3.0.0-incubating
      */
     max {
@@ -112,10 +118,24 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * Applies "and" to boolean values.
      *
+     * <pre>
+     *     a = true, b = null -> true
+     *     a = false, b = null -> false
+     *     a = null, b = true -> true
+     *     a = null, b = false -> false
+     *     a = null, b = null -> null
+     * </pre>
+     *
      * @since 3.2.0-incubating
      */
     and {
         public Object apply(final Object a, final Object b) {
+            if (null == a || null == b) {
+                if (null == a && null == b)
+                    return null;
+                else
+                    return null == b ? a : b;
+            }
             return ((boolean) a) && ((boolean) b);
         }
     },
@@ -123,25 +143,52 @@ public enum Operator implements BinaryOperator<Object> {
     /**
      * Applies "or" to boolean values.
      *
+     * <pre>
+     *     a = true, b = null -> true
+     *     a = false, b = null -> false
+     *     a = null, b = true -> true
+     *     a = null, b = false -> false
+     *     a = null, b = null -> null
+     * </pre>
+     *
      * @since 3.2.0-incubating
      */
     or {
         public Object apply(final Object a, final Object b) {
+            if (null == a || null == b) {
+                if (null == a && null == b)
+                    return null;
+                else
+                    return null == b ? a : b;
+            }
             return ((boolean) a) || ((boolean) b);
         }
     },
 
     /**
-     * Takes all objects in the second {@code Collection} and adds them to the first.
+     * Takes all objects in the second {@code Collection} and adds them to the first. If the first is {@code null},
+     * then the second {@code Collection} is returned and if the second is {@code null} then the first is returned.
+     * If both are {@code null} then {@code null} is returned. Arguments must be of type {@code Map} or
+     * {@code Collection}.
      *
      * @since 3.2.0-incubating
      */
     addAll {
         public Object apply(final Object a, final Object b) {
-            if (a instanceof Map)
+            if (null == a || null == b) {
+                if (null == a && null == b)
+                    return null;
+                else
+                    return null == b ? a : b;
+            }
+
+            if (a instanceof Map && b instanceof Map)
                 ((Map<?,?>) a).putAll((Map) b);
-            else
+            else if (a instanceof Collection && a instanceof Collection)
                 ((Collection<?>) a).addAll((Collection) b);
+            else
+                throw new IllegalArgumentException(String.format("Objects must be both of Map or Collection: a=%s b=%s",
+                        a.getClass().getSimpleName(), b.getClass().getSimpleName()));
             return a;
         }
     },
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SideEffectStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SideEffectStrategy.java
index 430f5ab..ce794b8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SideEffectStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SideEffectStrategy.java
@@ -60,7 +60,7 @@ public final class SideEffectStrategy extends AbstractTraversalStrategy<Traversa
             strategy = cloneStrategy;
             traversalStrategies.addStrategies(strategy);
         }
-        strategy.sideEffects.add(new Triplet<>(key, null == value ? null : value instanceof Supplier ? (Supplier) value : new ConstantSupplier<>(value), reducer));
+        strategy.sideEffects.add(new Triplet<>(key, value instanceof Supplier ? (Supplier) value : new ConstantSupplier<>(value), reducer));
     }
 
     public boolean contains(final String sideEffectKey) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java
index b512cf3..52f92a5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java
@@ -77,7 +77,6 @@ public class DefaultTraversalSideEffects implements TraversalSideEffects {
      */
     @Override
     public void set(final String key, final Object value) throws IllegalArgumentException {
-        SideEffectHelper.validateSideEffectValue(value);
         if (!this.keys.contains(key))
             throw TraversalSideEffects.Exceptions.sideEffectKeyDoesNotExist(key);
         this.objectMap.put(key, value);
@@ -88,7 +87,6 @@ public class DefaultTraversalSideEffects implements TraversalSideEffects {
      */
     @Override
     public void add(final String key, final Object value) throws IllegalArgumentException {
-        SideEffectHelper.validateSideEffectValue(value);
         this.set(key, this.getReducer(key).apply(this.get(key), value));
     }
 
@@ -99,7 +97,6 @@ public class DefaultTraversalSideEffects implements TraversalSideEffects {
     public <V> void register(final String key, final Supplier<V> initialValue, final BinaryOperator<V> reducer) {
         SideEffectHelper.validateSideEffectKey(key);
         this.keys.add(key);
-        //this.objectMap.remove(key);
         if (null != initialValue)
             this.supplierMap.put(key, initialValue);
         if (null != reducer)
@@ -113,7 +110,6 @@ public class DefaultTraversalSideEffects implements TraversalSideEffects {
     public <V> void registerIfAbsent(final String key, final Supplier<V> initialValue, final BinaryOperator<V> reducer) {
         SideEffectHelper.validateSideEffectKey(key);
         this.keys.add(key);
-        //this.objectMap.remove(key);
         if (null == this.supplierMap.get(key) && null != initialValue) {
             this.supplierMap.put(key, initialValue);
         }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OperatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OperatorTest.java
index e498cae..ce433c6 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OperatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OperatorTest.java
@@ -19,6 +19,7 @@
 package org.apache.tinkerpop.gremlin.process.traversal;
 
 import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -26,15 +27,241 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-@RunWith(Parameterized.class)
+@RunWith(Enclosed.class)
 public class OperatorTest {
 
+    @RunWith(Parameterized.class)
+    public static class LogicTest {
+        @Parameterized.Parameters(name = "{0}({1},{2}) = {3}")
+        public static Iterable<Object[]> data() {
+            return new ArrayList<>(Arrays.asList(new Object[][]{
+                    {Operator.and, true, true, true},
+                    {Operator.and, true, false, false},
+                    {Operator.and, false, true, false},
+                    {Operator.and, false, false, false},
+                    {Operator.and, null, null, null},
+                    {Operator.and, null, false, false},
+                    {Operator.and, false, null, false},
+                    {Operator.and, null, true, true},
+                    {Operator.and, true, null, true},
+                    {Operator.or, true, true, true},
+                    {Operator.or, true, false, true},
+                    {Operator.or, false, true, true},
+                    {Operator.or, false, false, false},
+                    {Operator.or, null, null, null},
+                    {Operator.or, null, false, false},
+                    {Operator.or, false, null, false},
+                    {Operator.or, null, true, true},
+                    {Operator.or, true, null, true},
+            }));
+        }
+
+        @Parameterized.Parameter(value = 0)
+        public Operator operator;
+
+        @Parameterized.Parameter(value = 1)
+        public Boolean a;
+
+        @Parameterized.Parameter(value = 2)
+        public Boolean b;
+
+        @Parameterized.Parameter(value = 3)
+        public Boolean expected;
+
+        @Test
+        public void shouldApplyOperator() {
+            assertEquals(expected, operator.apply(a, b));
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class CollectionTest {
+        @Parameterized.Parameters(name = "{0}({1},{2}) = {3}")
+        public static Iterable<Object[]> data() {
+            return new ArrayList<>(Arrays.asList(new Object[][]{
+                    {Operator.addAll, null, null, null},
+                    {Operator.addAll, new ArrayList<>(Arrays.asList(1,2,3)), null, Arrays.asList(1,2,3)},
+                    {Operator.addAll, null, new ArrayList<>(Arrays.asList(1,2,3)), Arrays.asList(1,2,3)},
+                    {Operator.addAll, new ArrayList<>(Arrays.asList(1,2,3)), new ArrayList<>(Arrays.asList(4,5,6)), Arrays.asList(1,2,3,4,5,6)},
+            }));
+        }
+
+        @Parameterized.Parameter(value = 0)
+        public Operator operator;
+
+        @Parameterized.Parameter(value = 1)
+        public Collection a;
+
+        @Parameterized.Parameter(value = 2)
+        public Collection b;
+
+        @Parameterized.Parameter(value = 3)
+        public Collection expected;
+
+        @Test
+        public void shouldApplyOperator() {
+            assertEquals(expected, operator.apply(a, b));
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class MapTest {
+        @Parameterized.Parameters(name = "{0}({1},{2}) = {3}")
+        public static Iterable<Object[]> data() {
+            return new ArrayList<>(Arrays.asList(new Object[][]{
+                    {Operator.addAll, null, null, null},
+                    {Operator.addAll, new HashMap<String,Integer>() {{ put("a", 1); }}, null, new HashMap<String,Integer>() {{ put("a", 1); }}},
+                    {Operator.addAll, null, new HashMap<String,Integer>() {{ put("a", 1); }}, new HashMap<String,Integer>() {{ put("a", 1); }}},
+                    {Operator.addAll, new HashMap<String,Integer>() {{ put("a", 1); }}, new HashMap<String,Integer>() {{ put("b", 2); }}, new HashMap<String,Integer>() {{ put("a", 1); put("b", 2); }}},
+                    {Operator.addAll, new HashMap<String,Integer>() {{ put("a", 1); }}, new HashMap<String,Integer>() {{ put("a", 2); }}, new HashMap<String,Integer>() {{ put("a", 2); }}},
+            }));
+        }
+
+        @Parameterized.Parameter(value = 0)
+        public Operator operator;
+
+        @Parameterized.Parameter(value = 1)
+        public Map a;
+
+        @Parameterized.Parameter(value = 2)
+        public Map b;
+
+        @Parameterized.Parameter(value = 3)
+        public Map expected;
+
+        @Test
+        public void shouldApplyOperator() {
+            assertEquals(expected, operator.apply(a, b));
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class NumberTest {
+        @Parameterized.Parameters(name = "{0}({1},{2}) = {3}")
+        public static Iterable<Object[]> data() {
+            return new ArrayList<>(Arrays.asList(new Object[][]{
+                    {Operator.div, 10, 2, 5},
+                    {Operator.div, 10l, 2l, 5l},
+                    {Operator.div, 10f, 2f, 5f},
+                    {Operator.div, 10d, 2d, 5d},
+                    {Operator.div, 10, 2d, 5d},
+                    {Operator.div, 10, 2f, 5f},
+                    {Operator.div, 10d, 2, 5d},
+                    {Operator.div, 10f, 2d, 5d},
+                    {Operator.div, 10f, 2l, 5d},
+                    {Operator.div, 10f, 2, 5f},
+                    {Operator.div, BigInteger.TEN, 2, BigInteger.valueOf(5l)},
+                    {Operator.div, BigInteger.TEN, 2f, BigDecimal.valueOf(5l)},
+                    {Operator.div, BigDecimal.TEN, 2, BigDecimal.valueOf(5l)},
+                    {Operator.div, CustomNumber.TEN, 2, BigDecimal.valueOf(5l)},
+                    {Operator.max, 10, 2, 10},
+                    {Operator.max, 10l, 2l, 10l},
+                    {Operator.max, 10f, 2f, 10f},
+                    {Operator.max, 10d, 2d, 10d},
+                    {Operator.max, 2, 10, 10},
+                    {Operator.max, 2l, 10l, 10l},
+                    {Operator.max, 2f, 10f, 10f},
+                    {Operator.max, 2d, 10d, 10d},
+                    {Operator.max, 10, 2d, 10d},
+                    {Operator.max, 10, 2f, 10f},
+                    {Operator.max, 10d, 2, 10d},
+                    {Operator.max, 10f, 2d, 10d},
+                    {Operator.max, 10f, 2l, 10d},
+                    {Operator.max, 10f, 2, 10f},
+                    {Operator.max, BigInteger.TEN, 1, BigInteger.TEN},
+                    {Operator.max, BigInteger.TEN, BigDecimal.ONE, BigDecimal.TEN},
+                    {Operator.max, BigDecimal.TEN, 1, BigDecimal.TEN},
+                    {Operator.max, 1, CustomNumber.TEN, BigDecimal.TEN},
+                    {Operator.min, 10, 2, 2},
+                    {Operator.min, 10l, 2l, 2l},
+                    {Operator.min, 10f, 2f, 2f},
+                    {Operator.min, 10d, 2d, 2d},
+                    {Operator.min, 2, 10, 2},
+                    {Operator.min, 2l, 10l, 2l},
+                    {Operator.min, 2f, 10f, 2f},
+                    {Operator.min, 2d, 10d, 2d},
+                    {Operator.min, 10, 2d, 2d},
+                    {Operator.min, 10, 2f, 2f},
+                    {Operator.min, 10d, 2, 2d},
+                    {Operator.min, 10f, 2d, 2d},
+                    {Operator.min, 10f, 2l, 2d},
+                    {Operator.min, 10f, 2, 2f},
+                    {Operator.min, BigInteger.TEN, 1, BigInteger.ONE},
+                    {Operator.min, BigInteger.TEN, BigDecimal.ONE, BigDecimal.ONE},
+                    {Operator.min, BigDecimal.TEN, 1, BigDecimal.ONE},
+                    {Operator.min, 1, CustomNumber.TEN, BigDecimal.ONE},
+                    {Operator.minus, 10, 2, 8},
+                    {Operator.minus, 10l, 2l, 8l},
+                    {Operator.minus, 10f, 2f, 8f},
+                    {Operator.minus, 10d, 2d, 8d},
+                    {Operator.minus, 10, 2d, 8d},
+                    {Operator.minus, 10, 2f, 8f},
+                    {Operator.minus, 10d, 2, 8d},
+                    {Operator.minus, 10f, 2d, 8d},
+                    {Operator.minus, 10f, 2l, 8d},
+                    {Operator.minus, 10f, 2, 8f},
+                    {Operator.minus, BigInteger.TEN, 2, BigInteger.valueOf(8l)},
+                    {Operator.minus, BigInteger.TEN, 2f, BigDecimal.valueOf(8d)},
+                    {Operator.minus, BigDecimal.TEN, 2, BigDecimal.valueOf(8l)},
+                    {Operator.minus, CustomNumber.TEN, 2, BigDecimal.valueOf(8l)},
+                    {Operator.mult, 5, 4, 20},
+                    {Operator.mult, 5l, 4l, 20l},
+                    {Operator.mult, 5f, 4f, 20f},
+                    {Operator.mult, 5d, 4d, 20d},
+                    {Operator.mult, 5, 4d, 20d},
+                    {Operator.mult, 5, 4f, 20f},
+                    {Operator.mult, 5d, 4, 20d},
+                    {Operator.mult, 5f, 4d, 20d},
+                    {Operator.mult, 5f, 4l, 20d},
+                    {Operator.mult, 5f, 4, 20f},
+                    {Operator.mult, BigInteger.valueOf(5l), 4, BigInteger.valueOf(20l)},
+                    {Operator.mult, BigInteger.valueOf(5l), 4f, BigDecimal.valueOf(20d)},
+                    {Operator.mult, BigDecimal.valueOf(5d), 4, BigDecimal.valueOf(20d)},
+                    {Operator.mult, CustomNumber.TEN, 2f, BigDecimal.valueOf(20d)},
+                    {Operator.sum, 7, 3, 10},
+                    {Operator.sum, 7l, 3l, 10l},
+                    {Operator.sum, 7f, 3f, 10f},
+                    {Operator.sum, 7d, 3d, 10d},
+                    {Operator.sum, 7, 3d, 10d},
+                    {Operator.sum, 7, 3f, 10f},
+                    {Operator.sum, 7d, 3, 10d},
+                    {Operator.sum, 7f, 3d, 10d},
+                    {Operator.sum, 7f, 3l, 10d},
+                    {Operator.sum, 7f, 3, 10f},
+                    {Operator.sum, BigInteger.valueOf(7l), 3, BigInteger.TEN},
+                    {Operator.sum, BigInteger.valueOf(7l), 3f, BigDecimal.valueOf(10d)},
+                    {Operator.sum, BigDecimal.valueOf(7d), 3, BigDecimal.valueOf(10d)},
+                    {Operator.sum, CustomNumber.TEN, CustomNumber.ONE, BigDecimal.valueOf(11l)}
+            }));
+        }
+
+        @Parameterized.Parameter(value = 0)
+        public Operator operator;
+
+        @Parameterized.Parameter(value = 1)
+        public Number a;
+
+        @Parameterized.Parameter(value = 2)
+        public Number b;
+
+        @Parameterized.Parameter(value = 3)
+        public Number expected;
+
+        @Test
+        public void shouldApplyOperator() {
+            assertEquals(expected, operator.apply(a, b));
+        }
+    }
+
     /**
      * Required to verify that Operator can handle Number type, that it doesn't know explicitly.
      */
@@ -74,119 +301,4 @@ public class OperatorTest {
             return Integer.compare(n, anotherCustomNumber.n);
         }
     }
-
-    @Parameterized.Parameters(name = "{0}({1},{2}) = {3}")
-    public static Iterable<Object[]> data() {
-        return new ArrayList<>(Arrays.asList(new Object[][]{
-                {Operator.div, 10, 2, 5},
-                {Operator.div, 10l, 2l, 5l},
-                {Operator.div, 10f, 2f, 5f},
-                {Operator.div, 10d, 2d, 5d},
-                {Operator.div, 10, 2d, 5d},
-                {Operator.div, 10, 2f, 5f},
-                {Operator.div, 10d, 2, 5d},
-                {Operator.div, 10f, 2d, 5d},
-                {Operator.div, 10f, 2l, 5d},
-                {Operator.div, 10f, 2, 5f},
-                {Operator.div, BigInteger.TEN, 2, BigInteger.valueOf(5l)},
-                {Operator.div, BigInteger.TEN, 2f, BigDecimal.valueOf(5l)},
-                {Operator.div, BigDecimal.TEN, 2, BigDecimal.valueOf(5l)},
-                {Operator.div, CustomNumber.TEN, 2, BigDecimal.valueOf(5l)},
-                {Operator.max, 10, 2, 10},
-                {Operator.max, 10l, 2l, 10l},
-                {Operator.max, 10f, 2f, 10f},
-                {Operator.max, 10d, 2d, 10d},
-                {Operator.max, 2, 10, 10},
-                {Operator.max, 2l, 10l, 10l},
-                {Operator.max, 2f, 10f, 10f},
-                {Operator.max, 2d, 10d, 10d},
-                {Operator.max, 10, 2d, 10d},
-                {Operator.max, 10, 2f, 10f},
-                {Operator.max, 10d, 2, 10d},
-                {Operator.max, 10f, 2d, 10d},
-                {Operator.max, 10f, 2l, 10d},
-                {Operator.max, 10f, 2, 10f},
-                {Operator.max, BigInteger.TEN, 1, BigInteger.TEN},
-                {Operator.max, BigInteger.TEN, BigDecimal.ONE, BigDecimal.TEN},
-                {Operator.max, BigDecimal.TEN, 1, BigDecimal.TEN},
-                {Operator.max, 1, CustomNumber.TEN, BigDecimal.TEN},
-                {Operator.min, 10, 2, 2},
-                {Operator.min, 10l, 2l, 2l},
-                {Operator.min, 10f, 2f, 2f},
-                {Operator.min, 10d, 2d, 2d},
-                {Operator.min, 2, 10, 2},
-                {Operator.min, 2l, 10l, 2l},
-                {Operator.min, 2f, 10f, 2f},
-                {Operator.min, 2d, 10d, 2d},
-                {Operator.min, 10, 2d, 2d},
-                {Operator.min, 10, 2f, 2f},
-                {Operator.min, 10d, 2, 2d},
-                {Operator.min, 10f, 2d, 2d},
-                {Operator.min, 10f, 2l, 2d},
-                {Operator.min, 10f, 2, 2f},
-                {Operator.min, BigInteger.TEN, 1, BigInteger.ONE},
-                {Operator.min, BigInteger.TEN, BigDecimal.ONE, BigDecimal.ONE},
-                {Operator.min, BigDecimal.TEN, 1, BigDecimal.ONE},
-                {Operator.min, 1, CustomNumber.TEN, BigDecimal.ONE},
-                {Operator.minus, 10, 2, 8},
-                {Operator.minus, 10l, 2l, 8l},
-                {Operator.minus, 10f, 2f, 8f},
-                {Operator.minus, 10d, 2d, 8d},
-                {Operator.minus, 10, 2d, 8d},
-                {Operator.minus, 10, 2f, 8f},
-                {Operator.minus, 10d, 2, 8d},
-                {Operator.minus, 10f, 2d, 8d},
-                {Operator.minus, 10f, 2l, 8d},
-                {Operator.minus, 10f, 2, 8f},
-                {Operator.minus, BigInteger.TEN, 2, BigInteger.valueOf(8l)},
-                {Operator.minus, BigInteger.TEN, 2f, BigDecimal.valueOf(8d)},
-                {Operator.minus, BigDecimal.TEN, 2, BigDecimal.valueOf(8l)},
-                {Operator.minus, CustomNumber.TEN, 2, BigDecimal.valueOf(8l)},
-                {Operator.mult, 5, 4, 20},
-                {Operator.mult, 5l, 4l, 20l},
-                {Operator.mult, 5f, 4f, 20f},
-                {Operator.mult, 5d, 4d, 20d},
-                {Operator.mult, 5, 4d, 20d},
-                {Operator.mult, 5, 4f, 20f},
-                {Operator.mult, 5d, 4, 20d},
-                {Operator.mult, 5f, 4d, 20d},
-                {Operator.mult, 5f, 4l, 20d},
-                {Operator.mult, 5f, 4, 20f},
-                {Operator.mult, BigInteger.valueOf(5l), 4, BigInteger.valueOf(20l)},
-                {Operator.mult, BigInteger.valueOf(5l), 4f, BigDecimal.valueOf(20d)},
-                {Operator.mult, BigDecimal.valueOf(5d), 4, BigDecimal.valueOf(20d)},
-                {Operator.mult, CustomNumber.TEN, 2f, BigDecimal.valueOf(20d)},
-                {Operator.sum, 7, 3, 10},
-                {Operator.sum, 7l, 3l, 10l},
-                {Operator.sum, 7f, 3f, 10f},
-                {Operator.sum, 7d, 3d, 10d},
-                {Operator.sum, 7, 3d, 10d},
-                {Operator.sum, 7, 3f, 10f},
-                {Operator.sum, 7d, 3, 10d},
-                {Operator.sum, 7f, 3d, 10d},
-                {Operator.sum, 7f, 3l, 10d},
-                {Operator.sum, 7f, 3, 10f},
-                {Operator.sum, BigInteger.valueOf(7l), 3, BigInteger.TEN},
-                {Operator.sum, BigInteger.valueOf(7l), 3f, BigDecimal.valueOf(10d)},
-                {Operator.sum, BigDecimal.valueOf(7d), 3, BigDecimal.valueOf(10d)},
-                {Operator.sum, CustomNumber.TEN, CustomNumber.ONE, BigDecimal.valueOf(11l)}
-        }));
-    }
-
-    @Parameterized.Parameter(value = 0)
-    public Operator operator;
-
-    @Parameterized.Parameter(value = 1)
-    public Number a;
-
-    @Parameterized.Parameter(value = 2)
-    public Number b;
-
-    @Parameterized.Parameter(value = 3)
-    public Number expected;
-
-    @Test
-    public void shouldApplyOperator() {
-        assertEquals(expected, operator.apply(a, b));
-    }
 }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffectsTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffectsTest.java
index 679042d..9d1210f 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffectsTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffectsTest.java
@@ -26,8 +26,11 @@ import org.junit.Test;
 
 import java.util.Arrays;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -37,6 +40,29 @@ import static org.junit.Assert.fail;
 public class DefaultTraversalSideEffectsTest {
 
     @Test
+    public void shouldAllowNulls() {
+        final TraversalSideEffects sideEffects = new DefaultTraversalSideEffects();
+        sideEffects.register("k", new ConstantSupplier<>(null), null);
+        assertNull(sideEffects.get("k"));
+
+        sideEffects.set("k", "x");
+        assertEquals("x", sideEffects.get("k"));
+
+        sideEffects.set("k", null);
+        assertNull(sideEffects.get("k"));
+
+        sideEffects.add("k", null);
+        assertNull(sideEffects.get("k"));
+
+        sideEffects.register("kand", new ConstantSupplier<>(null), Operator.and);
+        sideEffects.add("kand", null);
+        assertNull(sideEffects.get("kand"));
+
+        sideEffects.add("kand", true);
+        assertThat(sideEffects.get("kand"), is(true));
+    }
+
+    @Test
     public void shouldOperateCorrectly() {
         final TraversalSideEffects sideEffects = new DefaultTraversalSideEffects();
         try {
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 6e606ea..74b346c 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -522,7 +522,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_hasXageX_propertiesXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("name").Value<object>()}}, 
                {"g_V_hasXageX_propertiesXname_ageX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("name","age").Value<object>()}}, 
                {"g_V_hasXageX_propertiesXage_nameX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("age","name").Value<object>()}}, 
-               {"get_g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a","b")}}, 
+               {"g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a","b")}}, 
                {"g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a","b").By("name")}}, 
                {"g_VX1X_asXaX_outXknowsX_asXbX_selectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a")}}, 
                {"g_VX1X_asXaX_outXknowsX_asXbX_selectXaX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a").By("name")}}, 
@@ -550,7 +550,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_outE_weight_groupCount_unfold_selectXvaluesX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().OutE().Values<object>("weight").GroupCount<object>().Unfold<object>().Select<object>(Column.Values).Unfold<object>()}}, 
                {"g_V_untilXout_outX_repeatXin_asXaX_in_asXbXX_selectXa_bX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Until(__.Out().Out()).Repeat(__.In().As("a").In().As("b")).Select<object>("a","b").By("name")}}, 
                {"g_V_outE_weight_groupCount_selectXvaluesX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().OutE().Values<object>("weight").GroupCount<object>().Select<object>(Column.Values).Unfold<object>()}}, 
-               {"g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a","b")}}, 
                {"g_V_asXaX_whereXoutXknowsXX_selectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Where(__.Out("knows")).Select<object>("a")}}, 
                {"g_VX1X_asXaX_repeatXout_asXaXX_timesX2X_selectXfirst_aX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Repeat(__.Out().As("a")).Times(2).Select<object>(Pop.First,"a")}}, 
                {"g_V_asXaX_outXknowsX_asXbX_localXselectXa_bX_byXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Out("knows").As("b").Local<object>(__.Select<object>("a","b").By("name"))}}, 
@@ -586,6 +585,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E(p["eid11"]).Properties<object>("weight").As("a").Select<object>("a").By(T.Key)}}, 
                {"g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E(p["eid11"]).Properties<object>("weight").As("a").Select<object>("a").By(T.Value)}}, 
                {"g_V_asXaX_selectXaX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Select<object>("a").By("age")}}, 
+               {"g_withSideEffectXk_nullX_injectXxX_selectXkX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSideEffect("k",null).Inject("x").Select<object>("k")}}, 
                {"g_V_shortestPath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new VertexProgramStrategy(graphComputer: "org.apache.tinkerpop.gremlin.process.computer.GraphComputer")).V().Identity().ShortestPath()}}, 
                {"g_V_both_dedup_shortestPath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new VertexProgramStrategy(graphComputer: "org.apache.tinkerpop.gremlin.process.computer.GraphComputer")).V().Both().Dedup().ShortestPath()}}, 
                {"g_V_shortestPath_edgesIncluded", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new VertexProgramStrategy(graphComputer: "org.apache.tinkerpop.gremlin.process.computer.GraphComputer")).V().Identity().ShortestPath().With("~tinkerpop.shortestPath.includeEdges")}}, 
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 c3f01ac..f0b3c50 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
@@ -511,7 +511,7 @@ const gremlins = {
     g_V_hasXageX_propertiesXnameX: [function({g}) { return g.V().has("age").properties("name").value() }], 
     g_V_hasXageX_propertiesXname_ageX_value: [function({g}) { return g.V().has("age").properties("name","age").value() }], 
     g_V_hasXageX_propertiesXage_nameX_value: [function({g}) { return g.V().has("age").properties("age","name").value() }], 
-    get_g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX: [function({g, vid1}) { return g.V(vid1).as("a").out("knows").as("b").select("a","b") }], 
+    g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX: [function({g, vid1}) { return g.V(vid1).as("a").out("knows").as("b").select("a","b") }], 
     g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX_byXnameX: [function({g, vid1}) { return g.V(vid1).as("a").out("knows").as("b").select("a","b").by("name") }], 
     g_VX1X_asXaX_outXknowsX_asXbX_selectXaX: [function({g, vid1}) { return g.V(vid1).as("a").out("knows").as("b").select("a") }], 
     g_VX1X_asXaX_outXknowsX_asXbX_selectXaX_byXnameX: [function({g, vid1}) { return g.V(vid1).as("a").out("knows").as("b").select("a").by("name") }], 
@@ -539,7 +539,6 @@ const gremlins = {
     g_V_outE_weight_groupCount_unfold_selectXvaluesX_unfold: [function({g}) { return g.V().outE().values("weight").groupCount().unfold().select(Column.values).unfold() }], 
     g_V_untilXout_outX_repeatXin_asXaX_in_asXbXX_selectXa_bX_byXnameX: [function({g}) { return g.V().until(__.out().out()).repeat(__.in_().as("a").in_().as("b")).select("a","b").by("name") }], 
     g_V_outE_weight_groupCount_selectXvaluesX_unfold: [function({g}) { return g.V().outE().values("weight").groupCount().select(Column.values).unfold() }], 
-    g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX: [function({g, vid1}) { return g.V(vid1).as("a").out("knows").as("b").select("a","b") }], 
     g_V_asXaX_whereXoutXknowsXX_selectXaX: [function({g}) { return g.V().as("a").where(__.out("knows")).select("a") }], 
     g_VX1X_asXaX_repeatXout_asXaXX_timesX2X_selectXfirst_aX: [function({g, vid1}) { return g.V(vid1).as("a").repeat(__.out().as("a")).times(2).select(Pop.first,"a") }], 
     g_V_asXaX_outXknowsX_asXbX_localXselectXa_bX_byXnameXX: [function({g}) { return g.V().as("a").out("knows").as("b").local(__.select("a","b").by("name")) }], 
@@ -575,6 +574,7 @@ const gremlins = {
     g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX: [function({g, eid11}) { return g.E(eid11).properties("weight").as("a").select("a").by(T.key) }], 
     g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX: [function({g, eid11}) { return g.E(eid11).properties("weight").as("a").select("a").by(T.value) }], 
     g_V_asXaX_selectXaX_byXageX: [function({g}) { return g.V().as("a").select("a").by("age") }], 
+    g_withSideEffectXk_nullX_injectXxX_selectXkX: [function({g}) { return g.withSideEffect("k",null).inject("x").select("k") }], 
     g_V_shortestPath: [function({g}) { return g.withStrategies(new VertexProgramStrategy(new Map([["graphComputer","org.apache.tinkerpop.gremlin.process.computer.GraphComputer"]]))).V().identity().shortestPath() }], 
     g_V_both_dedup_shortestPath: [function({g}) { return g.withStrategies(new VertexProgramStrategy(new Map([["graphComputer","org.apache.tinkerpop.gremlin.process.computer.GraphComputer"]]))).V().both().dedup().shortestPath() }], 
     g_V_shortestPath_edgesIncluded: [function({g}) { return g.withStrategies(new VertexProgramStrategy(new Map([["graphComputer","org.apache.tinkerpop.gremlin.process.computer.GraphComputer"]]))).V().identity().shortestPath().with_("~tinkerpop.shortestPath.includeEdges") }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index bfab55e..ac17b3b 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -496,7 +496,7 @@ world.gremlins = {
     'g_V_hasXageX_propertiesXnameX': [(lambda g:g.V().has('age').properties('name').value())], 
     'g_V_hasXageX_propertiesXname_ageX_value': [(lambda g:g.V().has('age').properties('name','age').value())], 
     'g_V_hasXageX_propertiesXage_nameX_value': [(lambda g:g.V().has('age').properties('age','name').value())], 
-    'get_g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX': [(lambda g, vid1=None:g.V(vid1).as_('a').out('knows').as_('b').select('a','b'))], 
+    'g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX': [(lambda g, vid1=None:g.V(vid1).as_('a').out('knows').as_('b').select('a','b'))], 
     'g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX_byXnameX': [(lambda g, vid1=None:g.V(vid1).as_('a').out('knows').as_('b').select('a','b').by('name'))], 
     'g_VX1X_asXaX_outXknowsX_asXbX_selectXaX': [(lambda g, vid1=None:g.V(vid1).as_('a').out('knows').as_('b').select('a'))], 
     'g_VX1X_asXaX_outXknowsX_asXbX_selectXaX_byXnameX': [(lambda g, vid1=None:g.V(vid1).as_('a').out('knows').as_('b').select('a').by('name'))], 
@@ -524,7 +524,6 @@ world.gremlins = {
     'g_V_outE_weight_groupCount_unfold_selectXvaluesX_unfold': [(lambda g:g.V().outE().weight.groupCount().unfold().select(Column.values).unfold())], 
     'g_V_untilXout_outX_repeatXin_asXaX_in_asXbXX_selectXa_bX_byXnameX': [(lambda g:g.V().until(__.out().out()).repeat(__.in_().as_('a').in_().as_('b')).select('a','b').by('name'))], 
     'g_V_outE_weight_groupCount_selectXvaluesX_unfold': [(lambda g:g.V().outE().weight.groupCount().select(Column.values).unfold())], 
-    'g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX': [(lambda g, vid1=None:g.V(vid1).as_('a').out('knows').as_('b').select('a','b'))], 
     'g_V_asXaX_whereXoutXknowsXX_selectXaX': [(lambda g:g.V().as_('a').where(__.out('knows')).select('a'))], 
     'g_VX1X_asXaX_repeatXout_asXaXX_timesX2X_selectXfirst_aX': [(lambda g, vid1=None:g.V(vid1).as_('a').repeat(__.out().as_('a')).times(2).select(Pop.first,'a'))], 
     'g_V_asXaX_outXknowsX_asXbX_localXselectXa_bX_byXnameXX': [(lambda g:g.V().as_('a').out('knows').as_('b').local(__.select('a','b').by('name')))], 
@@ -560,6 +559,7 @@ world.gremlins = {
     'g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX': [(lambda g, eid11=None:g.E(eid11).properties('weight').as_('a').select('a').by(T.key))], 
     'g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX': [(lambda g, eid11=None:g.E(eid11).properties('weight').as_('a').select('a').by(T.value))], 
     'g_V_asXaX_selectXaX_byXageX': [(lambda g:g.V().as_('a').select('a').by('age'))], 
+    'g_withSideEffectXk_nullX_injectXxX_selectXkX': [(lambda g:g.withSideEffect('k',None).inject('x').select('k'))], 
     'g_V_shortestPath': [(lambda g:g.withStrategies(*[TraversalStrategy('VertexProgramStrategy',{'graphComputer':'org.apache.tinkerpop.gremlin.process.computer.GraphComputer'}, 'org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy')]).V().identity().shortestPath())], 
     'g_V_both_dedup_shortestPath': [(lambda g:g.withStrategies(*[TraversalStrategy('VertexProgramStrategy',{'graphComputer':'org.apache.tinkerpop.gremlin.process.computer.GraphComputer'}, 'org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy')]).V().both().dedup().shortestPath())], 
     'g_V_shortestPath_edgesIncluded': [(lambda g:g.withStrategies(*[TraversalStrategy('VertexProgramStrategy',{'graphComputer':'org.apache.tinkerpop.gremlin.process.computer.GraphComputer'}, 'org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy')]).V().identity().shortestPath().with_('~tinkerpop.shortestPath.includeEdges'))], 
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java
index 54ab3df..f0d34dc 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java
@@ -104,6 +104,10 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
         method = "g_injectXlistXnull_10_20_nullXX_meanXlocalX",
         reason = "The inject() step is not supported by GraphComputer")
 @Graph.OptOut(
+        test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest",
+        method = "g_withSideEffectXk_nullX_injectXxX_selectXkX",
+        reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
         test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
         method = "g_injectXnull_10_20_nullX_mean",
         reason = "The inject() step is not supported by GraphComputer")
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java
index 2678738..4932dde 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java
@@ -108,6 +108,10 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
         method = "g_injectXnull_10_20_nullX_mean",
         reason = "The inject() step is not supported by GraphComputer")
 @Graph.OptOut(
+        test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest",
+        method = "g_withSideEffectXk_nullX_injectXxX_selectXkX",
+        reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
         test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
         method = "*",
         reason = "The io() step is not supported generally by GraphComputer")
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java
index 3d5c510..1240741 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java
@@ -109,6 +109,10 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
         method = "g_injectXnull_10_20_nullX_mean",
         reason = "The inject() step is not supported by GraphComputer")
 @Graph.OptOut(
+        test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest",
+        method = "g_withSideEffectXk_nullX_injectXxX_selectXkX",
+        reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
         test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
         method = "*",
         reason = "The io() step is not supported generally by GraphComputer")
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java
index 01b3a21..924992d 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java
@@ -111,6 +111,10 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
         method = "g_injectXnull_10_20_nullX_mean",
         reason = "The inject() step is not supported by GraphComputer")
 @Graph.OptOut(
+        test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest",
+        method = "g_withSideEffectXk_nullX_injectXxX_selectXkX",
+        reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
         test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
         method = "*",
         reason = "The io() step is not supported generally by GraphComputer")
diff --git a/gremlin-test/features/map/Select.feature b/gremlin-test/features/map/Select.feature
index 3169e0e..6da8ac6 100644
--- a/gremlin-test/features/map/Select.feature
+++ b/gremlin-test/features/map/Select.feature
@@ -17,7 +17,7 @@
 
 Feature: Step - select()
 
-  Scenario: get_g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX
+  Scenario: g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX
     Given the modern graph
     And using the parameter vid1 defined as "v[marko].id"
     And the traversal of
@@ -436,19 +436,6 @@ Feature: Step - select()
       | d[2].l |
       | d[1].l |
 
-  Scenario: g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX
-    Given the modern graph
-    And using the parameter vid1 defined as "v[marko].id"
-    And the traversal of
-      """
-      g.V(vid1).as("a").out("knows").as("b").select("a", "b")
-      """
-    When iterated to list
-    Then the result should be unordered
-      | result |
-      | m[{"a":"v[marko]","b":"v[vadas]"}] |
-      | m[{"a":"v[marko]","b":"v[josh]"}] |
-
   Scenario: g_V_asXaX_whereXoutXknowsXX_selectXaX
     Given the modern graph
     And the traversal of
@@ -830,4 +817,15 @@ Feature: Step - select()
       | null |
       | d[32].i |
       | null |
-      | d[35].i |
\ No newline at end of file
+      | d[35].i |
+
+  Scenario: g_withSideEffectXk_nullX_injectXxX_selectXkX
+    Given the empty graph
+    And the traversal of
+      """
+      g.withSideEffect("k",null).inject("x").select("k")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | null |
\ No newline at end of file
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
index f6c8d12..4b450ca 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
@@ -47,8 +47,11 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.as;
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.identity;
 import static org.apache.tinkerpop.gremlin.structure.Column.keys;
 import static org.apache.tinkerpop.gremlin.structure.Column.values;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -162,6 +165,8 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Edge, Object> get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX(final Object e11Id);
 
+    public abstract Traversal<String, Object> get_g_withSideEffectXk_nullX_injectXxX_selectXkX();
+
     // when labels don't exist
 
     public abstract Traversal<Vertex, String> get_g_V_untilXout_outX_repeatXin_asXaXX_selectXaX_byXtailXlocalX_nameX();
@@ -896,6 +901,14 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
         checkResults(Arrays.asList(29, 27, null, 32, null, 35), traversal);
     }
 
+    @Test
+    public void g_withSideEffectXk_nullX_injectXxX_selectXkX() {
+        final Traversal<String, Object> traversal = get_g_withSideEffectXk_nullX_injectXxX_selectXkX();
+        printTraversalForm(traversal);
+        assertNull(traversal.next());
+        assertThat(traversal.hasNext(), is(false));
+    }
+
     public static class Traversals extends SelectTest {
         @Override
         public Traversal<Vertex, Map<String, Vertex>> get_g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX(final Object v1Id) {
@@ -1224,5 +1237,10 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, Object> get_g_V_asXaX_selectXaX_byXageX() {
             return g.V().as("a").select("a").by("age");
         }
+
+        @Override
+        public Traversal<String, Object> get_g_withSideEffectXk_nullX_injectXxX_selectXkX() {
+            return g.withSideEffect("k",null).inject("x").select("k");
+        }
     }
 }