type List { List addToFront(Any); List addToEnd(Any); Any first(); Any atIndex(int); Any last(); List rest(); List drop(int); List take(int); List appendToEnd(List); List appendToFront(List); List reverse(); int length(); bool empty(); List map(Any -> Any); Any foldl( -> Any, Any); Any foldr( -> Any, Any); List filter(Any -> bool); void foreach(Any -> void); } class MTList() : List impl List { List add(Any elt) { return new ConsList(car = elt, cdr = (this : List)); } Any no_elts(int n) { error("The empty list has no elements!"); } Any no_elt() { error("The empty list has no elements!"); } List rest() { error("Cannot get the rest of an empty list!"); } List drop(int n) { if n == 0 { this : List; } else { error("Attempt to drop elements from an empty list!"); }; } List take(int n) { if n == 0 { this : List; } else { error("Attempt to take elements from an empty list!"); }; } List ret_other(List l) { return l; } List reverse() { return (this : List); } int length() { return 0; } bool empty() { return true; } List map(Any -> Any f) { return (this : List); } Any fold( -> Any f, Any i) { return i; } List filter(Any -> bool f) { return (this : List); } void foreach(Any -> void f) { return(); } export List : add as addToFront, add as addToEnd, no_elt as first, no_elts as atIndex, no_elt as last, rest, drop, take, reverse, ret_other as appendToEnd, ret_other as appendToFront, length, empty, map, fold as foldl, fold as foldr, filter, foreach; } // Since init slots get translated to init fields by need, we can put // car and cdr here, and then use them appropriately inside the methods // which will make them fields. class ConsList(Any car, List cdr) : List impl List { Any first() { return car; } Any atIndex(int n) { if n == 0 { car; } else { cdr.atIndex(n - 1); }; } Any last() { if cdr.empty() { car; } else { cdr.last(); }; } List rest() { return cdr; } List drop(int n) { if n == 0 { this : List; } else { cdr.drop(n - 1); }; } List take(int n) { if n == 0 { new MTList(); } else { new ConsList(car = car, cdr = cdr.take(n - 1)); }; } List addToFront(Any x) { return new ConsList(car = x, cdr = (this : List)); } List addToEnd(Any x) { return new ConsList(car = car, cdr = cdr.addToEnd(x)); } List appendToFront(List other) { if other.empty() { this : List; } else { new ConsList(car = other.first(), cdr = other.drop(1)); }; } List appendToEnd(List other) { return new ConsList(car = car, cdr = cdr.appendToEnd(other)); } List reverse() { return cdr.reverse().addToEnd(car); } int length() { return 1 + cdr.length(); } bool empty() { return false; } List map(Any -> Any f) { return new ConsList(car = f(car), cdr = cdr.map(f)); } Any foldl( -> Any f, Any i) { return f(car, cdr.foldl(f, i)); } Any foldr( -> Any f, Any i) { return cdr.foldr(f, f(car, i)); } List filter(Any -> bool f) { if f(car) { new ConsList(car = car, cdr = cdr.filter(f)); } else { cdr.filter(f); }; } void foreach(Any -> void f) { f(car); cdr.foreach(f); } export List : addToFront, addToEnd, first, atIndex, last, reverse, rest, drop, take, appendToEnd, appendToFront, length, empty, map, foldl, foldr, filter, foreach; }