CurriedFunctions.java

package com.github.dakusui.pcond.experimentals.currying;

import com.github.dakusui.pcond.experimentals.currying.context.CurriedContext;
import com.github.dakusui.pcond.experimentals.currying.context.CurriedContextUtils;
import com.github.dakusui.pcond.core.printable.ParameterizedFunctionFactory;
import com.github.dakusui.pcond.core.printable.ParameterizedPredicateFactory;
import com.github.dakusui.pcond.core.printable.PrintablePredicateFactory;
import com.github.dakusui.pcond.forms.Printables;

import java.util.Collection;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static com.github.dakusui.pcond.internals.InternalUtils.formatObject;

/**
 * A class that collects methods to create functions and predicates in experimental stage.
 */
public enum CurriedFunctions {
  ;

  /**
   * This function is used to construct a function which replaces 'nested loop' in a usual programming.
   *
   * @param inner A collection for the "inner loop".
   * @return A function to construct a nested structure.
   */
  public static Function<Stream<?>, Stream<CurriedContext>> nest(Collection<?> inner) {
    return Printables.function(() -> "nest" + formatObject(inner), (Stream<?> stream) -> CurriedContextUtils.nest(stream, inner));
  }

  /**
   * Returns a converter function for a stream.
   * The function converts a stream of objects into and returns a stream of contexts each of which hols a value
   * from the original stream.
   *
   * @return A function to convert an object stream into context stream.
   * @see CurriedContext
   */
  public static Function<Stream<?>, Stream<CurriedContext>> toCurriedContextStream() {
    return Printables.function(() -> "toCurriedContextStream", CurriedContextUtils::toCurriedContextStream);
  }

  /**
   * Returns a function to convert a value into a context that holds the original value.
   *
   * @param <T> The type of the original value.
   * @return A function to convert a value into a context.
   */
  public static <T> Function<T, CurriedContext> toCurriedContext() {
    return Printables.function(() -> "toCurriedContext", CurriedContextUtils::toCurriedContext);
  }

  /**
   * Creates a context function that tests the value at the specified index using the given predicate.
   *
   * @param predicate_ A predicate with which the value is tested.
   * @param argIndex   An index to specify a value in the context.
   * @param <T>        An expected type of value to be tested.
   * @return A new predicate to test a value in a context.
   */
  public static <T> Predicate<CurriedContext> toCurriedContextPredicate(Predicate<T> predicate_, int argIndex) {
    return PrintablePredicateFactory.variableBundlePredicate(predicate_, argIndex);
  }

  /**
   * Converts a predicate to a context predicate which tests the first value in a context
   * using the `predicate`.
   *
   * @param predicate A predicate to be converted
   * @param <T>       An expected type of the input value.
   * @return A context predicate.
   */
  public static <T> Predicate<CurriedContext> toCurriedContextPredicate(Predicate<T> predicate) {
    return toCurriedContextPredicate(predicate, 0);
  }

  /**
   * Converts a curried function which results in a boolean value into a predicate.
   *
   * @param curriedFunction A curried function to be converted.
   * @param orderArgs       An array to specify the order in which values in the context are applied to the function.
   * @return A predicate converted from the given curried function.
   */
  public static Predicate<CurriedContext> toCurriedContextPredicate(CurriedFunction<Object, Object> curriedFunction, int... orderArgs) {
    return CurriedContextUtils.toContextPredicate(curriedFunction, orderArgs);
  }

  /**
   * Returns a builder for a factory to create a predicate.
   * The factory accepts an argument to create a new predicate.
   * With this method, you can create predicates with different values.
   *
   * Suppose, you are about to create a predicate that tests a given string starts with `"hello"`.
   * At the same time, you also want to create a predicate that checks the value using `"こんにちは"`.
   * You can do it with the factory built by the returned value of this method.
   *
   * @param name The name of the predicate. It will be followed by the value passed to the factory in the `pcond`'s output.
   * @param <T>  The expected type of the value to be tested by the final predicate.
   * @return A builder to create a predicate factory.
   */
  public static <T> ParameterizedPredicateFactory.Builder<T> parameterizedPredicate(String name) {
    return new ParameterizedPredicateFactory.Builder<T>().name(name);
  }

  /**
   * Returns a builder for a factory to create a function.
   *
   * @param name The name of the function. It will be followed by the value passed to the factory in the `pcond`'s output.
   * @param <T>  The expected type of the input value to the final function.
   * @param <R>  The expected type of the output value of the final function.
   * @return A builder to create a function factory.
   * @see CurriedFunctions#parameterizedPredicate(String)
   */
  public static <T, R> ParameterizedFunctionFactory.Builder<T, R> parameterizedFunction(String name) {
    return new ParameterizedFunctionFactory.Builder<T, R>().name(name);
  }
}