Merge branch '3.4-dev'
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index f77412c..61145c1 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -100,6 +100,7 @@
 * Bumped Netty 4.1.52.
 * Provided a more concise syntax for constructing strategies in Groovy.
 * Aligned `CoreImports` with `GroovyTranslator` to generate more succinct syntax.
+* Improved `gremlin-groovy` understanding of `withSack()` overloads to avoid forced casts.
 * Moved `Translator` instances to `gremlin-core`.
 * Added `CheckedGraphManager` to prevent Gremlin Server from starting if there are no graphs configured.
 * Fixed bug in bytecode `Bindings` where calling `of()` prior to calling a child traversal in the same parent would cause the initial binding to be lost.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
index b57ffc7..3a1ec42 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
@@ -28,6 +28,8 @@
 import org.apache.tinkerpop.gremlin.process.traversal.Script;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
@@ -43,15 +45,13 @@
 import java.math.BigInteger;
 import java.sql.Timestamp;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
-import java.util.function.BinaryOperator;
-import java.util.function.Supplier;
-import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
 
 /**
@@ -323,34 +323,13 @@
                 } else {
                     script.append(".").append(methodName).append("(");
 
-                    // have to special case withSack() for Groovy because UnaryOperator and BinaryOperator signatures
-                    // make it impossible for the interpreter to figure out which function to call. specifically we need
-                    // to discern between:
-                    //     withSack(A initialValue, UnaryOperator<A> splitOperator)
-                    //     withSack(A initialValue, BinaryOperator<A> splitOperator)
-                    // and:
-                    //     withSack(Supplier<A> initialValue, UnaryOperator<A> mergeOperator)
-                    //     withSack(Supplier<A> initialValue, BinaryOperator<A> mergeOperator)
-                    if (methodName.equals(TraversalSource.Symbols.withSack) &&
-                            instruction.getArguments().length == 2 && instruction.getArguments()[1] instanceof Lambda) {
-                        final String castFirstArgTo = instruction.getArguments()[0] instanceof Lambda ?
-                                Supplier.class.getName() : "";
-                        final Lambda secondArg = (Lambda) instruction.getArguments()[1];
-                        final String castSecondArgTo = secondArg.getLambdaArguments() == 1 ? UnaryOperator.class.getName() :
-                                BinaryOperator.class.getName();
-                        if (!castFirstArgTo.isEmpty())
-                            script.append(String.format("(%s) ", castFirstArgTo));
-                        convertToScript(instruction.getArguments()[0]);
-                        script.append(", (").append(castSecondArgTo).append(") ");
-                        convertToScript(instruction.getArguments()[1]);
-                        script.append(",");
-                    } else {
-                        for (final Object object : instruction.getArguments()) {
-                            convertToScript(object);
-                            script.append(",");
-                        }
+                    final Iterator<Object> itty = Arrays.stream(instruction.getArguments()).iterator();
+                    while(itty.hasNext()) {
+                        convertToScript(itty.next());
+                        if (itty.hasNext()) script.append(",");
                     }
-                    script.setCharAtEnd(')');
+
+                    script.append(")");
                 }
             }
             return script;
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java
index ac282f8..82d679d 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java
@@ -62,7 +62,7 @@
     private static final Translator.ScriptTranslator translator = GroovyTranslator.of("g");
 
     @Test
-    public void shouldTranslateStrategies() throws Exception {
+    public void shouldTranslateStrategies() {
         assertEquals("g.withStrategies(ReadOnlyStrategy,new SubgraphStrategy(checkAdjacentVertices: false, vertices: __.hasLabel(\"person\"))).V().has(\"name\")",
                 translator.translate(g.withStrategies(ReadOnlyStrategy.instance(),
                         SubgraphStrategy.build().checkAdjacentVertices(false).vertices(hasLabel("person")).create()).
@@ -73,19 +73,19 @@
     public void shouldTranslateConfusingSacks() {
         final Traversal<Vertex,Double> tConstantUnary = g.withSack(1.0, Lambda.unaryOperator("it + 1")).V().sack();
         final String scriptConstantUnary = translator.translate(tConstantUnary).getScript();
-        assertEquals("g.withSack(1.0d, (java.util.function.UnaryOperator) {it + 1}).V().sack()", scriptConstantUnary);
+        assertEquals("g.withSack(1.0d,{it + 1}).V().sack()", scriptConstantUnary);
 
         final Traversal<Vertex,Double> tSupplierUnary = g.withSack(Lambda.supplier("1.0d"), Lambda.<Double>unaryOperator("it + 1")).V().sack();
         final String scriptSupplierUnary = translator.translate(tSupplierUnary).getScript();
-        assertEquals("g.withSack((java.util.function.Supplier) {1.0d}, (java.util.function.UnaryOperator) {it + 1}).V().sack()", scriptSupplierUnary);
+        assertEquals("g.withSack({1.0d},{it + 1}).V().sack()", scriptSupplierUnary);
 
         final Traversal<Vertex,Double> tConstantBinary = g.withSack(1.0, Lambda.binaryOperator("x,y -> x + y + 1")).V().sack();
         final String scriptConstantBinary = translator.translate(tConstantBinary).getScript();
-        assertEquals("g.withSack(1.0d, (java.util.function.BinaryOperator) {x,y -> x + y + 1}).V().sack()", scriptConstantBinary);
+        assertEquals("g.withSack(1.0d,{x,y -> x + y + 1}).V().sack()", scriptConstantBinary);
 
         final Traversal<Vertex,Double> tSupplierBinary = g.withSack(Lambda.supplier("1.0d"), Lambda.<Double>binaryOperator("x,y -> x + y + 1")).V().sack();
         final String scriptSupplierBinary = translator.translate(tSupplierBinary).getScript();
-        assertEquals("g.withSack((java.util.function.Supplier) {1.0d}, (java.util.function.BinaryOperator) {x,y -> x + y + 1}).V().sack()", scriptSupplierBinary);
+        assertEquals("g.withSack({1.0d},{x,y -> x + y + 1}).V().sack()", scriptSupplierBinary);
     }
 
     @Test
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/StepLoader.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/StepLoader.groovy
index 8a91695..f0c59e5 100644
--- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/StepLoader.groovy
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/StepLoader.groovy
@@ -56,8 +56,11 @@
             return ((TraversalSource) delegate).withSack(closure as Supplier);
         }
 
-        TraversalSource.metaClass.withSack = { final Closure closure, final Closure splitOrMergeOperator ->
-            return ((TraversalSource) delegate).withSack(closure as Supplier, splitOrMergeOperator.getMaximumNumberOfParameters() == 1 ? splitOrMergeOperator as UnaryOperator : splitOrMergeOperator as BinaryOperator);
+        TraversalSource.metaClass.withSack = { final Object closureOrConstant, final Closure splitOrMergeOperator ->
+            if (closureOrConstant instanceof Closure)
+                return ((TraversalSource) delegate).withSack(closureOrConstant as Supplier, splitOrMergeOperator.getMaximumNumberOfParameters() == 1 ? splitOrMergeOperator as UnaryOperator : splitOrMergeOperator as BinaryOperator);
+            else
+                return ((TraversalSource) delegate).withSack((Object) closureOrConstant, splitOrMergeOperator.getMaximumNumberOfParameters() == 1 ? splitOrMergeOperator as UnaryOperator : splitOrMergeOperator as BinaryOperator);
         }
 
         TraversalSource.metaClass.withSack = {
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineOverGraphTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineOverGraphTest.java
index 43f8bc9..ae822e2 100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineOverGraphTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineOverGraphTest.java
@@ -347,6 +347,22 @@
         assertEquals(utf8Name, eval.next());
     }
 
+    @Test
+    public void shouldHandleSacksConfusingToGroovy() throws Exception {
+        final Graph graph = TinkerFactory.createModern();
+        final GraphTraversalSource g = graph.traversal();
+        final ScriptEngine engine = new GremlinGroovyScriptEngine();
+        engine.put("g", g);
+        List<Object> list = (List<Object>) engine.eval("g.withSack(1.0d,{it + 1}).V().sack().toList()");
+        assertEquals(6, list.size());
+        list = (List<Object>) engine.eval("g.withSack({1.0d},{it + 1}).V().sack().toList()");
+        assertEquals(6, list.size());
+        list = (List<Object>) engine.eval("g.withSack(1.0d,{x,y -> x + y + 1}).V().sack().toList()");
+        assertEquals(6, list.size());
+        list = (List<Object>) engine.eval("g.withSack({1.0d},{x,y -> x + y + 1}).V().sack().toList()");
+        assertEquals(6, list.size());
+    }
+
     private Object convertToVertexId(final Graph graph, final String vertexName) {
         return convertToVertex(graph, vertexName).id();
     }