diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
index cb47c0ec4bff90da4adaa81fb71d0e52eecd23d0..5fbdec5c2d2a2baea2679a1195997b02473fe803 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
@@ -23,23 +23,20 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.SlowPath;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.r.nodes.*;
-import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.builtin.*;
-import com.oracle.truffle.r.nodes.builtin.RBuiltin.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 
-@RBuiltin(name = "paste", kind = SUBSTITUTE, lastParameterKind = LastParameterKind.VAR_ARGS_SPECIALIZE)
-// TODO INTERNAL
+@RBuiltin(name = "paste", kind = INTERNAL)
 public abstract class Paste extends RBuiltinNode {
 
-    public abstract Object executeObject(VirtualFrame frame, Object value, String sep, Object collapse);
+    public abstract Object executeList(VirtualFrame frame, RList value, String sep, Object collapse);
 
     @Child CastStringNode castCharacterNode;
 
@@ -64,125 +61,17 @@ public abstract class Paste extends RBuiltinNode {
         }
     }
 
-    private static final Object[] PARAMETER_NAMES = new Object[]{"...", "sep", "collapse"};
-
-    @Override
-    public Object[] getParameterNames() {
-        return PARAMETER_NAMES;
-    }
-
-    @Override
-    public RNode[] getParameterValues() {
-        return new RNode[]{null, ConstantNode.create(" "), ConstantNode.create(RNull.instance)};
-    }
-
-    @Specialization
-    @SuppressWarnings("unused")
-    public RStringVector paste(RMissing value, Object sep, Object collapse) {
-        controlVisibility();
-        return RDataFactory.createEmptyStringVector();
-    }
-
-    @Specialization
-    @SuppressWarnings("unused")
-    public RStringVector paste(RNull value, Object sep, Object collapse) {
-        controlVisibility();
-        return RDataFactory.createEmptyStringVector();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    @SlowPath
-    public String paste(int value, Object sep, Object collapse) {
-        controlVisibility();
-        return String.valueOf(value);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    @SlowPath
-    public String paste(double value, Object sep, Object collapse) {
-        controlVisibility();
-        return String.valueOf(value);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public String paste(byte value, Object sep, Object collapse) {
-        controlVisibility();
-        return RRuntime.logicalToString(value);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public String paste(String value, Object sep, Object collapse) {
-        controlVisibility();
-        return value;
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public String paste(RComplex value, Object sep, Object collapse) {
-        controlVisibility();
-        return RRuntime.toString(value);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public RStringVector paste(RStringVector vector, Object sep, Object collapse) {
-        controlVisibility();
-        return checkCollapse(vector, collapse);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public RStringVector paste(VirtualFrame frame, RIntVector vector, Object sep, Object collapse) {
-        controlVisibility();
-        return checkCollapse(castCharacterVector(frame, vector), collapse);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public RStringVector paste(VirtualFrame frame, RDoubleVector vector, Object sep, Object collapse) {
-        controlVisibility();
-        return checkCollapse(castCharacterVector(frame, vector), collapse);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public RStringVector paste(VirtualFrame frame, RIntSequence sequence, Object sep, Object collapse) {
-        controlVisibility();
-        return checkCollapse(castCharacterVector(frame, sequence), collapse);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public RStringVector paste(VirtualFrame frame, RDoubleSequence sequence, Object sep, Object collapse) {
-        controlVisibility();
-        return checkCollapse(castCharacterVector(frame, sequence), collapse);
-    }
-
-    @SuppressWarnings("unused")
     @Specialization
-    public RStringVector paste(VirtualFrame frame, RLogicalVector vector, Object sep, Object collapse) {
+    public RStringVector pasteList(VirtualFrame frame, RList values, String sep, Object collapse) {
         controlVisibility();
-        return checkCollapse(castCharacterVector(frame, vector), collapse);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    public RStringVector paste(VirtualFrame frame, RList list, Object sep, Object collapse) {
-        controlVisibility();
-        return checkCollapse(castCharacterVector(frame, list), collapse);
-    }
-
-    @Specialization
-    public RStringVector paste(VirtualFrame frame, Object[] args, Object sep, Object collapse) {
-        controlVisibility();
-        Object[] converted = new Object[args.length];
+        if (isEmptyOrNull(values)) {
+            return RDataFactory.createEmptyStringVector();
+        }
+        int length = values.getLength();
+        Object[] converted = new Object[length];
         int maxLength = 1;
-        for (int i = 0; i < args.length; i++) {
-            Object element = args[i];
+        for (int i = 0; i < length; i++) {
+            Object element = values.getDataAt(i);
             if (element instanceof RVector || element instanceof RSequence) {
                 converted[i] = castCharacterVector(frame, element);
                 int len = ((RStringVector) converted[i]).getLength();
@@ -197,7 +86,7 @@ public abstract class Paste extends RBuiltinNode {
         String[] result = new String[maxLength];
         for (int i = 0; i < maxLength; i++) {
             StringBuilder builder = new StringBuilder();
-            for (int j = 0; j < args.length; j++) {
+            for (int j = 0; j < length; j++) {
                 if (j != 0) {
                     builder.append(sep);
                 }
@@ -214,13 +103,8 @@ public abstract class Paste extends RBuiltinNode {
         }
     }
 
-    private static RStringVector checkCollapse(RStringVector result, Object collapse) {
-        if (collapse != RNull.instance) {
-            String collapseString = RRuntime.toString(collapse);
-            return buildString(result, collapseString);
-        } else {
-            return result;
-        }
+    public boolean isEmptyOrNull(RList values) {
+        return values.getLength() == 0 || (values.getLength() == 1 && values.getDataAt(0) == RNull.instance);
     }
 
     private static RStringVector buildString(String[] value, String collapseString) {
@@ -234,17 +118,6 @@ public abstract class Paste extends RBuiltinNode {
         return RDataFactory.createStringVector(new String[]{buildStringToString(sb)}, RDataFactory.COMPLETE_VECTOR);
     }
 
-    private static RStringVector buildString(RStringVector vector, String collapseString) {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < vector.getLength(); i++) {
-            if (i > 0) {
-                sb.append(collapseString);
-            }
-            sb.append(vector.getDataAt(i));
-        }
-        return RDataFactory.createStringVector(new String[]{buildStringToString(sb)}, RDataFactory.COMPLETE_VECTOR);
-    }
-
     @SlowPath
     private static String buildStringToString(StringBuilder sb) {
         return sb.toString();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java
index 30f6b646bfc3bb76877723c4d2bf96c112dea7a7..f9002a7d65deb22c873c9735d48b01a486a14a23 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java
@@ -28,40 +28,29 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.*;
-import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.builtin.*;
-import com.oracle.truffle.r.nodes.builtin.RBuiltin.*;
 import com.oracle.truffle.r.runtime.data.*;
 
-@RBuiltin(name = "paste0", kind = SUBSTITUTE, lastParameterKind = LastParameterKind.VAR_ARGS_SPECIALIZE)
-// TODO INTERNAL
+/**
+ * A straightforward implementation in terms of {@code paste} that doesn't attempt to be more
+ * efficient.
+ */
+@RBuiltin(name = "paste0", kind = INTERNAL)
 public abstract class Paste0 extends RBuiltinNode {
 
     @Child Paste pasteNode;
 
-    private Object paste(VirtualFrame frame, Object value, Object collapse) {
+    private Object paste(VirtualFrame frame, RList values, Object collapse) {
         if (pasteNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             pasteNode = insert(PasteFactory.create(new RNode[1], getBuiltin()));
         }
-        return pasteNode.executeObject(frame, value, "", collapse);
-    }
-
-    private static final Object[] PARAMETER_NAMES = new Object[]{"...", "collapse"};
-
-    @Override
-    public Object[] getParameterNames() {
-        return PARAMETER_NAMES;
-    }
-
-    @Override
-    public RNode[] getParameterValues() {
-        return new RNode[]{null, ConstantNode.create(RNull.instance)};
+        return pasteNode.executeList(frame, values, "", collapse);
     }
 
     @Specialization
-    public Object paste0(VirtualFrame frame, Object value, Object collapse) {
+    public Object paste0(VirtualFrame frame, RList values, Object collapse) {
         controlVisibility();
-        return paste(frame, value, collapse);
+        return paste(frame, values, collapse);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/paste.R b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/paste.R
new file mode 100644
index 0000000000000000000000000000000000000000..45ea8c5ded41271208b740a31f2354f3d2f7c6cd
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/paste.R
@@ -0,0 +1,33 @@
+#  File src/library/base/R/paste.R
+#  Part of the R package, http://www.R-project.org
+#
+#  Copyright (C) 1995-2012 The R Core Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  A copy of the GNU General Public License is available at
+#  http://www.r-project.org/Licenses/
+
+paste <- function (..., sep = " ", collapse = NULL)
+  .Internal(paste(list(...), sep, collapse))
+paste0 <- function(..., collapse = NULL)
+  .Internal(paste0(list(...), collapse))
+
+##=== Could we extend  paste(.) to (optionally) accept a
+##    2-vector for collapse ?   With the following functionality
+
+##- paste.extra <- function(r, collapse=c(", "," and ")) {
+##-      n <- length(r)
+##-      if(n <= 1) paste(r)
+##-      else
+##-        paste(paste(r[-n],collapse=collapse[1L]),
+##-        r[n], sep=collapse[min(2,length(collapse))])
+##- }