All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.data.hlist.HList Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj.data.hlist;

import fj.F;
import fj.F2;
import fj.F3;
import fj.P;
import fj.P2;
import fj.Unit;
import static fj.Function.compose;

/**
 * Type-safe heterogeneous lists.
 *
 * @param  The specific type of the list, as a subtype of HList
 */
public abstract class HList> {

  HList() {
  }

  /**
   * Extends (cons) this list by prepending the given element, returning a new list.
   *
   * @param e an element to prepend to this list.
   * @return a new heterogeneous list, consisting of the given element prepended to this list.
   */
  public abstract  HCons extend(E e);

  public abstract  Apply, HCons> extender();

  /**
   * Returns the empty list.
   *
   * @return the empty list.
   */
  public static HNil nil() {
    return HNil.nil;
  }

  /**
   * Returns a heterogeneous list consisting of an element and another list.
   *
   * @param e an element to put in a list.
   * @param l the rest of the list.
   * @return a heterogeneous list consisting of an element and another list.
   */
  public static > HCons cons(final E e, final L l) {
    return new HCons<>(e, l);
  }

  /**
   * Returns a heterogeneous list consisting of a single element.
   *
   * @param e an element to put in a list
   * @return a heterogeneous list consisting of a single element.
   */
  public static  HCons single(final E e) {
    return cons(e, nil());
  }

  /**
   * The concatenation of two heterogeneous lists.
   *
   * @param  The type of the first list.
   * @param  The type of the second list.
   * @param  The type of the combined list.
   */
  public static final class HAppend {
    private final F2 append;

    private HAppend(final F2 f) {
      append = f;
    }

    /**
     * Append a given heterogeneous list to another.
     *
     * @param a a heterogeneous list to be appended to.
     * @param b a heterogeneous list to append to another.
     * @return a new heterogeneous list consisting of the second argument appended to the first.
     */
    public C append(final A a, final B b) {
      return append.f(a, b);
    }

    /**
     * Returns a method for concatenating lists to the empty list.
     *
     * @return a method for concatenating lists to the empty list.
     */
    public static > HAppend append() {
      return new HAppend<>((hNil, l) -> l);
    }

    /**
     * Returns a method for appending lists to a nonempty heterogeneous list.
     *
     * @param h a method for appending lists to the tail of the given nonempty list.
     * @return a method for appending lists to a nonempty heterogeneous list.
     */
    public static , B, C extends HList, H extends HAppend>
    HAppend, B, HCons> append(final H h) {
      return new HAppend<>((c, l) -> cons(c.head(), h.append(c.tail(), l)));
    }
  }

  /**
   * Type-level function application operators.
   *
   * @param  The type of the function to apply.
   * @param  The domain of the function.
   * @param  The function's codomain.
   */
  public abstract static class Apply {
    public abstract R apply(F$ f, A a);

    /**
     * Function application operator.
     *
     * @return an operator that applies a given function to a given argument.
     */
    public static  Apply, X, Y> f() {
      return new Apply, X, Y>() {
        public Y apply(final F f, final X x) {
          return f.f(x);
        }
      };
    }

    /**
     * Identity operator
     *
     * @return An operator that returns its second argument no matter which function is being applied.
     */
    public static  Apply id() {
      return new Apply() {
        public X apply(final Unit f, final X x) {
          return x;
        }
      };
    }

    /**
     * A function application operator for function composition.
     *
     * @param  The domain.
     * @param  The type through which to compose.
     * @param  The codomain.
     * @return an operator that composes functions.
     */
    public static  Apply, F>, F> comp() {
      return new Apply, F>, F>() {
        public F apply(final Unit f, final P2, F> fs) {
          return compose(fs._2(), fs._1());
        }
      };
    }

    /**
     * An operator for the construction of heterogeneous lists.
     *
     * @return an operator that constructs heterogeneous lists.
     */
    public static > Apply, HCons> cons() {
      return new Apply, HCons>() {
        public HCons apply(final Unit f, final P2 p) {
          return HList.cons(p._1(), p._2());
        }
      };
    }

    /**
     * A function application operator for concatenating heterogeneous lists.
     *
     * @param  The type of the list to which to append.
     * @param  The type of the list to append.
     * @param  The type of the concatenated list.
     * @return an operator that concatenates heterogeneous lists.
     */
    public static  Apply, P2, C> append() {
      return new Apply, P2, C>() {
        public C apply(final HAppend f, final P2 p) {
          return f.append(p._1(), p._2());
        }
      };
    }
  }

  /**
   * The catamorphism over heterogeneous lists.
   *
   * @param  The type of the function with which to fold.
   * @param  The type of the value to be substituted for the empty list.
   * @param  The type of the heterogeneous list to be folded.
   * @param  The return type of the fold.
   */
  public static final class HFoldr {

    private final F3 foldRight;

    private HFoldr(final F3 foldRight) {
      this.foldRight = foldRight;
    }

    /**
     * A fold instance for the empty list.
     *
     * @param  The type of the function with which to fold.
     * @param  The type of value that this fold returns.
     * @return a fold instance for the empty list.
     */
    public static  HFoldr hFoldr() {
      return new HFoldr<>((f, v, hNil) -> v);
    }

    /**
     * A fold instance for a non-empty heterogeneous list
     *
     * @param p    An operator that applies a function on the head of the list and the fold of its tail.
     * @param h    A fold instance for the tail of the list.
     * @param   The type of the head of the list.
     * @param   The type of function to apply to the head of the list and the fold of its tail.
     * @param   The type of value to substitute for the empty list.
     * @param   The type of the tail of the list.
     * @param   The type of the fold of the tail of the list.
     * @param  The return type of the fold.
     * @param   The type of the fold instance for the tail of the list.
     * @param  The type of the given function application operator.
     * @return A fold instance for a non-empty heterogeneous list.
     */
    public static , R, RR,
        H extends HFoldr,
        PP extends Apply, RR>>
    HFoldr, RR> hFoldr(final PP p, final H h) {
      return new HFoldr<>((f, v, c) -> p.apply(f, P.p(c.head(), h.foldRight(f, v, c.tail()))));
    }

    /**
     * Folds a non-empty heterogeneous list.
     *
     * @param f A function with which to fold.
     * @param v The value to substitute for the empty list.
     * @param l The heterogeneous list to be folded.
     * @return a value obtained by folding the given list with the given function.
     */
    public R foldRight(final G f, final V v, final L l) {
      return foldRight.f(f, v, l);
    }

  }

  /**
   * The nonempty list
   */
  public static final class HCons> extends HList> {
    private final E e;
    private final L l;

    HCons(final E e, final L l) {
      this.e = e;
      this.l = l;
    }

    public E head() {
      return e;
    }

    public L tail() {
      return l;
    }

    public  Apply>, HCons>> extender() {
      return Apply.cons();
    }

    public  HCons> extend(final X e) {
      return cons(e, this);
    }

  }

  /**
   * The empty list
   */
  public static final class HNil extends HList {

    private static final HNil nil = new HNil();

    private HNil() {
    }

    public  HCons extend(final E e) {
      return cons(e, this);
    }

    public  Apply, HCons> extender() {
      return Apply.cons();
    }

  }
}