diff --git a/common.mk b/common.mk
index 1884c44..f9738bb 100644
--- a/common.mk
+++ b/common.mk
@@ -55,6 +55,7 @@ COMMONOBJS    = array.$(OBJEXT) \
 		hash.$(OBJEXT) \
 		inits.$(OBJEXT) \
 		io.$(OBJEXT) \
+		list.$(OBJEXT) \
 		marshal.$(OBJEXT) \
 		math.$(OBJEXT) \
 		node.$(OBJEXT) \
@@ -648,6 +649,8 @@ inits.$(OBJEXT): {$(VPATH)}inits.c $(RUBY_H_INCLUDES) \
   {$(VPATH)}internal.h
 io.$(OBJEXT): {$(VPATH)}io.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \
   {$(VPATH)}util.h $(ENCODING_H_INCLUDES) {$(VPATH)}dln.h {$(VPATH)}internal.h
+list.$(OBJEXT): {$(VPATH)}list.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
+  $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h {$(VPATH)}node.h
 main.$(OBJEXT): {$(VPATH)}main.c $(RUBY_H_INCLUDES) {$(VPATH)}debug.h \
   {$(VPATH)}node.h
 marshal.$(OBJEXT): {$(VPATH)}marshal.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \
diff --git a/gc.c b/gc.c
index e9f3678..f03d796 100644
--- a/gc.c
+++ b/gc.c
@@ -298,6 +298,7 @@ typedef struct RVALUE {
 	struct RMatch  match;
 	struct RRational rational;
 	struct RComplex complex;
+	struct RCons cons;
     } as;
 #ifdef GC_DEBUG
     const char *file;
@@ -2094,6 +2095,11 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
 	gc_mark(objspace, obj->as.complex.imag, lev);
 	break;
 
+      case T_CONS:
+	gc_mark(objspace, obj->as.cons.car, lev);
+	gc_mark(objspace, obj->as.cons.cdr, lev);
+	break;
+
       case T_STRUCT:
 	{
 	    long len = RSTRUCT_LEN(obj);
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 6909bf3..cc33020 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -397,6 +397,7 @@ enum ruby_value_type {
     RUBY_T_MATCH  = 0x0d,
     RUBY_T_COMPLEX  = 0x0e,
     RUBY_T_RATIONAL = 0x0f,
+    RUBY_T_CONS = 0x10,
 
     RUBY_T_NIL    = 0x11,
     RUBY_T_TRUE   = 0x12,
@@ -434,6 +435,7 @@ enum ruby_value_type {
 #define T_SYMBOL RUBY_T_SYMBOL
 #define T_RATIONAL RUBY_T_RATIONAL
 #define T_COMPLEX RUBY_T_COMPLEX
+#define T_CONS RUBY_T_CONS
 #define T_UNDEF  RUBY_T_UNDEF
 #define T_NODE   RUBY_T_NODE
 #define T_ZOMBIE RUBY_T_ZOMBIE
@@ -775,6 +777,12 @@ struct RComplex {
     VALUE imag;
 };
 
+struct RCons {
+    struct RBasic basic;
+    VALUE car;
+    VALUE cdr;
+};
+
 struct RData {
     struct RBasic basic;
     void (*dmark)(void*);
@@ -934,6 +942,7 @@ struct RBignum {
 #define RFILE(obj)   (R_CAST(RFile)(obj))
 #define RRATIONAL(obj) (R_CAST(RRational)(obj))
 #define RCOMPLEX(obj) (R_CAST(RComplex)(obj))
+#define RCONS(obj)  (R_CAST(RCons)(obj))
 
 #define FL_SINGLETON FL_USER0
 #define FL_RESERVED1 (((VALUE)1)<<5)
@@ -1261,6 +1270,7 @@ RUBY_EXTERN VALUE rb_cArray;
 RUBY_EXTERN VALUE rb_cBignum;
 RUBY_EXTERN VALUE rb_cBinding;
 RUBY_EXTERN VALUE rb_cClass;
+RUBY_EXTERN VALUE rb_cCons;
 RUBY_EXTERN VALUE rb_cCont;
 RUBY_EXTERN VALUE rb_cDir;
 RUBY_EXTERN VALUE rb_cData;
@@ -1273,6 +1283,7 @@ RUBY_EXTERN VALUE rb_cFloat;
 RUBY_EXTERN VALUE rb_cHash;
 RUBY_EXTERN VALUE rb_cInteger;
 RUBY_EXTERN VALUE rb_cIO;
+RUBY_EXTERN VALUE rb_cList;
 RUBY_EXTERN VALUE rb_cMatch;
 RUBY_EXTERN VALUE rb_cMethod;
 RUBY_EXTERN VALUE rb_cModule;
diff --git a/inits.c b/inits.c
index 73b9eb4..a3859a3 100644
--- a/inits.c
+++ b/inits.c
@@ -59,6 +59,7 @@ rb_call_inits(void)
     CALL(Cont);
     CALL(Rational);
     CALL(Complex);
+    CALL(List);
     CALL(version);
 }
 #undef CALL
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..39e31b1
--- /dev/null
+++ b/list.c
@@ -0,0 +1,332 @@
+/**********************************************************************
+
+  list.c -
+
+  Copyright (C) 2012  Shugo Maeda
+
+**********************************************************************/
+
+#include "ruby/ruby.h"
+#include "ruby/util.h"
+#include "ruby/encoding.h"
+#include "internal.h"
+#include "node.h"
+
+VALUE rb_cCons;
+
+static ID id_each;
+static ID id_foldl;
+static ID id_foldr;
+
+static VALUE cons_new(VALUE car, VALUE cdr);
+
+static VALUE
+list_s_create(int argc, VALUE *argv, VALUE klass)
+{
+    if (argc == 0) {
+	return Qnil;
+    }
+    else {
+	int i;
+	VALUE cons = cons_new(argv[argc - 1], Qnil);
+
+	for (i = argc - 2; i >= 0; i--) {
+	    cons = cons_new(argv[i], cons);
+	}
+	return cons;
+    }
+}
+
+static VALUE
+list_cons(VALUE cdr, VALUE car)
+{
+    return cons_new(car, cdr);
+}
+
+static VALUE
+cons_alloc(VALUE klass)
+{
+    NEWOBJ(ary, struct RCons);
+    OBJSETUP(ary, klass, T_CONS);
+
+    return (VALUE)ary;
+}
+
+static VALUE
+cons_initialize(VALUE self, VALUE car, VALUE cdr)
+{
+    RCONS(self)->car = car;
+    RCONS(self)->cdr = cdr;
+    return Qnil;
+}
+
+static VALUE
+cons_new(VALUE car, VALUE cdr)
+{
+    VALUE cons;
+
+    cons = cons_alloc(rb_cCons);
+    cons_initialize(cons, car, cdr);
+    return cons;
+}
+
+static VALUE
+cons_car(VALUE self)
+{
+    return RCONS(self)->car;
+}
+
+static VALUE
+cons_cdr(VALUE self)
+{
+    return RCONS(self)->cdr;
+}
+
+static VALUE
+cons_each(VALUE self)
+{
+    rb_yield(RCONS(self)->car);
+    rb_funcall_passing_block(RCONS(self)->cdr, id_each, 0, 0);
+    return Qnil;
+}
+
+static VALUE
+nil_each(VALUE self)
+{
+    return Qnil;
+}
+
+static VALUE
+cons_foldl(VALUE self, VALUE x)
+{
+    VALUE y = rb_yield_values(2, x, RCONS(self)->car);
+    return rb_funcall_passing_block(RCONS(self)->cdr, id_foldl, 1, &y);
+}
+
+static VALUE
+nil_foldl(VALUE self, VALUE x)
+{
+    return x;
+}
+
+static VALUE
+cons_foldr(VALUE self, VALUE x)
+{
+    VALUE y = rb_funcall_passing_block(RCONS(self)->cdr, id_foldr, 1, &x);
+    return rb_yield_values(2, RCONS(self)->car, y);
+}
+
+static VALUE
+nil_foldr(VALUE self, VALUE x)
+{
+    return x;
+}
+
+static VALUE
+list_plus_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return cons_new(argv[0], argv[1]);
+}
+
+static VALUE
+list_plus(VALUE self, VALUE other)
+{
+    return rb_block_call(self, id_foldr, 1, &other, list_plus_i, Qnil);
+}
+
+static VALUE
+list_flatten_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return rb_funcall(argv[0], '+', 1, argv[1]);
+}
+
+static VALUE
+list_flatten(VALUE self)
+{
+    VALUE x = Qnil;
+
+    return rb_block_call(self, id_foldr, 1, &x, list_flatten_i, Qnil);
+}
+
+static VALUE
+list_map_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return cons_new(rb_yield(argv[0]), argv[1]);
+}
+
+static VALUE
+list_map(VALUE self)
+{
+    VALUE x = Qnil;
+
+    return rb_block_call(self, id_foldr, 1, &x, list_map_i, Qnil);
+}
+
+static VALUE
+list_flat_map_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return rb_funcall(rb_yield(argv[0]), '+', 1, argv[1]);
+}
+
+static VALUE
+list_flat_map(VALUE self)
+{
+    VALUE x = Qnil;
+
+    return rb_block_call(self, id_foldr, 1, &x, list_flat_map_i, Qnil);
+}
+
+static VALUE
+list_filter_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    if (RTEST(rb_yield(argv[0]))) {
+	return cons_new(argv[0], argv[1]);
+    }
+    else {
+	return argv[1];
+    }
+}
+
+static VALUE
+list_filter(VALUE self)
+{
+    VALUE x = Qnil;
+
+    return rb_block_call(self, id_foldr, 1, &x, list_filter_i, Qnil);
+}
+
+static VALUE
+list_length_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return LONG2NUM(NUM2LONG(argv[0]) + 1);
+}
+
+static VALUE
+list_length(VALUE self)
+{
+    VALUE n = LONG2NUM(0);
+
+    return rb_block_call(self, id_foldl, 1, &n, list_length_i, Qnil);
+}
+
+static VALUE
+list_sum_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return rb_funcall(argv[0], '+', 1, argv[1]);
+}
+
+static VALUE
+list_sum(VALUE self)
+{
+    VALUE n = LONG2NUM(0);
+
+    return rb_block_call(self, id_foldl, 1, &n, list_sum_i, Qnil);
+}
+
+static VALUE
+list_reverse_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+    }
+    return cons_new(argv[1], argv[0]);
+}
+
+static VALUE
+list_reverse(VALUE self)
+{
+    VALUE x = Qnil;
+
+    return rb_block_call(self, id_foldl, 1, &x, list_reverse_i, Qnil);
+}
+
+static VALUE
+cons_inspect_i(VALUE i, VALUE arg, int argc, VALUE *argv)
+{
+    NODE *memo = RNODE(arg);
+    VALUE str = memo->u1.value;
+    long is_first = memo->u3.state;
+
+    if (is_first) {
+	memo->u3.state = FALSE;
+    }
+    else {
+	rb_str_cat2(str, ", ");
+    }
+    rb_str_concat(str, rb_inspect(i));
+    return Qnil;
+}
+
+static VALUE
+cons_inspect(VALUE self)
+{
+    VALUE str = rb_str_new2("S[");
+    NODE *memo = NEW_MEMO(str, 0, TRUE);
+
+    rb_block_call(self, id_each, 0, 0, cons_inspect_i, (VALUE) memo);
+    rb_str_cat2(str, "]");
+    return str;
+}
+
+void
+Init_List(void)
+{
+    rb_define_global_const("S", rb_cList);
+    rb_include_module(rb_cList, rb_mEnumerable);
+
+    rb_define_singleton_method(rb_cList, "[]", list_s_create, -1);
+    rb_define_method(rb_cList, ":::", list_cons, 1);
+
+    rb_cCons  = rb_define_class("Cons", rb_cList);
+
+    rb_define_alloc_func(rb_cCons, cons_alloc);
+    rb_define_method(rb_cCons, "initialize", cons_initialize, 2);
+    rb_define_method(rb_cCons, "car", cons_car, 0);
+    rb_define_method(rb_cCons, "head", cons_car, 0);
+    rb_define_method(rb_cCons, "cdr", cons_cdr, 0);
+    rb_define_method(rb_cCons, "tail", cons_cdr, 0);
+    rb_define_method(rb_cCons, "each", cons_each, 0);
+    rb_define_method(rb_cNilClass, "each", nil_each, 0);
+    rb_define_method(rb_cCons, "foldl", cons_foldl, 1);
+    rb_define_method(rb_cNilClass, "foldl", nil_foldl, 1);
+    rb_define_method(rb_cCons, "foldr", cons_foldr, 1);
+    rb_define_method(rb_cNilClass, "foldr", nil_foldr, 1);
+    rb_define_method(rb_cList, "+", list_plus, 1);
+    rb_define_method(rb_cList, "flatten", list_flatten, 0);
+    rb_define_method(rb_cList, "concat", list_flatten, 0);
+    rb_define_method(rb_cList, "map", list_map, 0);
+    rb_define_method(rb_cList, "flat_map", list_flat_map, 0);
+    rb_define_method(rb_cList, "concat_map", list_flat_map, 0);
+    rb_define_method(rb_cList, "filter", list_filter, 0);
+    rb_define_method(rb_cList, "length", list_length, 0);
+    rb_define_method(rb_cList, "size", list_length, 0);
+    rb_define_method(rb_cList, "sum", list_sum, 0);
+    rb_define_method(rb_cList, "reverse", list_reverse, 0);
+
+    rb_define_method(rb_cCons, "inspect", cons_inspect, 0);
+
+    id_each = rb_intern("each");
+    id_foldl = rb_intern("foldl");
+    id_foldr = rb_intern("foldr");
+}
diff --git a/object.c b/object.c
index d43095d..f817446 100644
--- a/object.c
+++ b/object.c
@@ -30,6 +30,7 @@ VALUE rb_cModule;
 VALUE rb_cClass;
 VALUE rb_cData;
 
+VALUE rb_cList;
 VALUE rb_cNilClass;
 VALUE rb_cTrueClass;
 VALUE rb_cFalseClass;
@@ -2889,7 +2890,8 @@ Init_Object(void)
     rb_define_global_function("Array", rb_f_array, 1);
     rb_define_global_function("Hash", rb_f_hash, 1);
 
-    rb_cNilClass = rb_define_class("NilClass", rb_cObject);
+    rb_cList  = rb_define_class("List", rb_cObject);
+    rb_cNilClass = rb_define_class("NilClass", rb_cList);
     rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0);
     rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0);
     rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0);
diff --git a/parse.y b/parse.y
index 0393dc1..264d0bf 100644
--- a/parse.y
+++ b/parse.y
@@ -748,6 +748,7 @@ static void token_info_pop(struct parser_params*, const char *token);
 %token tLSHFT tRSHFT	/* << and >> */
 %token tCOLON2		/* :: */
 %token tCOLON3		/* :: at EXPR_BEG */
+%token tCOLON4		/* ::: */
 %token <id> tOP_ASGN	/* +=, -=  etc. */
 %token tASSOC		/* => */
 %token tLPAREN		/* ( */
@@ -776,6 +777,7 @@ static void token_info_pop(struct parser_params*, const char *token);
 %right '=' tOP_ASGN
 %left modifier_rescue
 %right '?' ':'
+%right tCOLON4
 %nonassoc tDOT2 tDOT3
 %left  tOROP
 %left  tANDOP
@@ -1920,6 +1922,7 @@ op		: '|'		{ ifndef_ripper($$ = '|'); }
 		| tAREF		{ ifndef_ripper($$ = tAREF); }
 		| tASET		{ ifndef_ripper($$ = tASET); }
 		| '`'		{ ifndef_ripper($$ = '`'); }
+		| tCOLON4	{ ifndef_ripper($$ = tCOLON4); }
 		;
 
 reswords	: keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
@@ -2125,6 +2128,14 @@ arg		: lhs '=' arg
 			$$ = dispatch1(assign_error, $$);
 		    %*/
 		    }
+		| arg tCOLON4 arg
+		    {
+		    /*%%%*/
+			$$ = call_bin_op($3, tCOLON4, $1);
+		    /*%
+			$$ = dispatch3(binary, $1, ID2SYM(tCOLON4), $3);
+		    %*/
+		    }
 		| arg tDOT2 arg
 		    {
 		    /*%%%*/
@@ -7522,6 +7533,11 @@ parser_yylex(struct parser_params *parser)
       case ':':
 	c = nextc();
 	if (c == ':') {
+	    c = nextc();
+	    if (c == ':') {
+		return tCOLON4;
+	    }
+	    pushback(c);
 	    if (IS_BEG() || lex_state == EXPR_CLASS || IS_SPCARG(-1)) {
 		lex_state = EXPR_BEG;
 		return tCOLON3;
@@ -9697,6 +9713,7 @@ static const struct {
     {tLSHFT,	"<<"},
     {tRSHFT,	">>"},
     {tCOLON2,	"::"},
+    {tCOLON4,	":::"},
 };
 
 #define op_tbl_count numberof(op_tbl)
diff --git a/template/id.h.tmpl b/template/id.h.tmpl
index 52c3685..5c863ec 100644
--- a/template/id.h.tmpl
+++ b/template/id.h.tmpl
@@ -23,7 +23,7 @@ tokens = nil
 vpath.find do |dir|
   begin
     if line = File.read(File.join(dir, input))[/^\s*enum\s+yytokentype\s*\{([^{}]*)\s*\};/m, 1]
-      tokens = line.scan(/\b(t(?:LAST_TOKEN|U(?:PLUS|MINUS)|POW|CMP|EQQ?|[NGL]EQ|(?:AND|OR)OP|N?MATCH|DOT\d|AREF|ASET|[LR]SHFT|LAMBDA)|id\w+)\s*=\s*(\d+),?/m)
+      tokens = line.scan(/\b(t(?:LAST_TOKEN|U(?:PLUS|MINUS)|POW|CMP|EQQ?|[NGL]EQ|(?:AND|OR)OP|N?MATCH|DOT\d|AREF|ASET|[LR]SHFT|COLON4|LAMBDA)|id\w+)\s*=\s*(\d+),?/m)
     end
   rescue Errno::ENOENT
     nil
@@ -105,6 +105,7 @@ enum ruby_method_ids {
     idNeqTilde = tNMATCH,
     idAREF = tAREF,
     idASET = tASET,
+    idColon4 = tCOLON4,
     idLAST_TOKEN = tLAST_TOKEN >> ID_SCOPE_SHIFT,
 % method_ids.each do |token|
     t<%=token%>,
