content: package lambdasinaction.chap14;
  
  import java.util.function.Supplier;
  import java.util.function.Predicate;
  
  public class LazyLists {
  
      public static void main(String[] args) {
          MyList<Integer> l = new MyLinkedList<>(5, new MyLinkedList<>(10,
                  new Empty<Integer>()));
  
          System.out.println(l.head());
  
          LazyList<Integer> numbers = from(2);
          int two = numbers.head();
          int three = numbers.tail().head();
          int four = numbers.tail().tail().head();
          System.out.println(two + " " + three + " " + four);
  
          numbers = from(2);
          int prime_two = primes(numbers).head();
          int prime_three = primes(numbers).tail().head();
          int prime_five = primes(numbers).tail().tail().head();
          System.out.println(prime_two + " " + prime_three + " " + prime_five);
  
          // this will run until a stackoverflow occur because Java does not
          // support tail call elimination
          // printAll(primes(from(2)));
      }
  
      interface MyList<T> {
          T head();
  
          MyList<T> tail();
  
          default boolean isEmpty() {
              return true;
          }
  
          MyList<T> filter(Predicate<T> p);
      }
  
      static class MyLinkedList<T> implements MyList<T> {
          final T head;
          final MyList<T> tail;
  
          public MyLinkedList(T head, MyList<T> tail) {
              this.head = head;
              this.tail = tail;
          }
  
          public T head() {
              return head;
          }
  
          public MyList<T> tail() {
              return tail;
          }
  
          public boolean isEmpty() {
              return false;
          }
  
          public MyList<T> filter(Predicate<T> p) {
              return isEmpty() ? this : p.test(head()) ? new MyLinkedList<>(
                      head(), tail().filter(p)) : tail().filter(p);
          }
      }
  
      static class Empty<T> implements MyList<T> {
          public T head() {
              throw new UnsupportedOperationException();
          }
  
          public MyList<T> tail() {
              throw new UnsupportedOperationException();
          }
  
          public MyList<T> filter(Predicate<T> p) {
              return this;
          }
      }
  
      static class LazyList<T> implements MyList<T> {
          final T head;
          final Supplier<MyList<T>> tail;
  
          public LazyList(T head, Supplier<MyList<T>> tail) {
              this.head = head;
              this.tail = tail;
          }
  
          public T head() {
              return head;
          }
  
          public MyList<T> tail() {
              return tail.get();
          }
  
          public boolean isEmpty() {
              return false;
          }
  
          public MyList<T> filter(Predicate<T> p) {
              return isEmpty() ? this : p.test(head()) ? new LazyList<>(head(),
                      () -> tail().filter(p)) : tail().filter(p);
          }
  
      }
  
      public static LazyList<Integer> from(int n) {
          return new LazyList<Integer>(n, () -> from(n + 1));
      }
  
      public static MyList<Integer> primes(MyList<Integer> numbers) {
          return new LazyList<>(numbers.head(), () -> primes(numbers.tail()
                  .filter(n -> n % numbers.head() != 0)));
      }
  
      static <T> void printAll(MyList<T> numbers) {
          if (numbers.isEmpty()) {
              return;
          }
          System.out.println(numbers.head());
          printAll(numbers.tail());
      }
  
  }
  