I wrote the following as more of an experiment than anything else, but I thought it would be fun to share and maybe get some feedback!Motivation: I started looking at some functional languages and noticed how useful the |> pipeline operator is. I started wondering how it might be translated to the Java language.There are many common functions omitted from the Stream library that could be useful in expressing logic in a more clear fashion. For example, Stream does not have an instance method to concat with another stream-- you have to use a static method. This means that you can't easily chain concat to another operation within a stream; you have to wrap it within a method call. More examples include  reversing, delays, zipping, etc. For each of these operations you have to make a helper method, and just like with concat you have to break chaining to wrap the partial solution within a method. This starts getting messy quickly, preventing authors from writing readable one-line expressions. Consider the following example where static and instance methods are called on a stream:List<Object> foo = Stream    .concat(        reverseStream(            list1.stream()                .map(Some::func)                .flatMap(other::stuff)),        list2.stream()            .map(Some::func)            .flatMap(other::stuff)))    .map(Some::otherFunc)    .collect(Collectors.toList()) ...With a pipeline |> operator, you could hide some complexity:List<Object> foo = list1 |> stream()    |> map(Some::func)    |> flatMap(other::stuff))    |> reverseStream()    |> concat(list1 |> stream()        |> map(Some::func)        |> flatMap(other::stuff))    |> map(Some::otherFunc)    |> collect(Collectors.toList());The following are the methods I wrote which enable piping t |> f:/** * Perform a pipe operation on a parameter and a function. * This follows the form: R = T |> F *  * @param t The parameter to be applied * @param f The function to be called * @param <T> The parameter type * @param <R> The result type * @return the result of f(t) */static <T, R> R pipe(final T t, final Function<T, R> f) {    return f.apply(t);}/** * Perform a pipe operation on a parameter and a consumer. * This follows the form: T |> F *  * @param t The parameter to be accepted * @param f The consumer to be called * @param <T> The parameter type */static <T> void pipe(final T t, final Consumer<T> f) {    f.accept(t);}pipe takes in a parameter and a method to accept this parameter. You can either choose to terminate the pipe with a Consumer or chain the pipe with a Function.For example list.stream().map(Some::func).collect(Collectors.toList()) can be adapted to list |> stream() |> map(Some::func) |> collect(Collectors.toList()) by defining static stream, map and collect functions:static <T> Function<Collection<T>, Stream<T>> stream() {    return Collection::stream;}static <T, R> Function<Stream<T>, Stream<R>> map(final Function<T, R> f) {    return s -> s.map(f);}static <T, A, R> Function<Stream<T>, R> collect(final Collector<? super T, A, R> c) {    return s -> s.collect(c);}After parsing the pipeline it should be able to generate the code:pipe(pipe(pipe(list, stream()), map(Some::func)), collect(Collectors.toList()))Bellow is a code generator I wrote to handle simple cases, such as nested pipelines. It evaluates pipeline expressions recursively from the top-level down, replacing all nested pipelines before the parent:/** * Parse a string representing a pipeline and generate java code  * which realizes the pipeline. Pipeline components at the top  * level are evaluated first, then each component is parsed to see if * it contains nested pipelines. *  * @param pipeline the text representation of the pipeline * @return the java code equivalent string */public static String buildPipeline(final String pipeline) {    return splitPipeline(pipeline).stream().map(p -> {        for (int index = 0, openBrace = 0, depth = 0; index < p.length(); index++) {            if (p.charAt(index) == '(' && depth++ == 0) {                openBrace = index;            }            else if (p.charAt(index) == ')' && --depth == 0) {                final int start = openBrace + 1, stop = index, lastLength = p.length();                p = p.substring(0, start)                        + buildPipeline(p.substring(start, stop))                        + p.substring(stop, lastLength);                index = p.length() - (lastLength - stop) + 1;            }        }        return p.trim();    }).reduce((accumulated, next) -> {        if (accumulated == null) {            return next;        }        return pipe( + accumulated + ,  + next + );    }).orElse(pipeline);}/** * Split a string based on the |> pipeline token. Only pipeline * tokens in the top level are evaluated, nested pipelines are  * ignored. *  * @param pipeline the text representation of the pipeline * @return a list of pipeline components */public static List<String> splitPipeline(final String pipeline) {    final List<String> splits = new LinkedList<>();    final StringBuilder builder = new StringBuilder();    for(int i = 0, depth = 0; i < pipeline.length(); i++) {        if(pipeline.charAt(i) == '(') {            depth++;        } else if(pipeline.charAt(i) == ')') {            depth--;        } else if(            pipeline.charAt(i) == '|'            && pipeline.charAt(i+1) == '>'            && depth == 0        ) {            splits.add(builder.toString());            builder.setLength(0);            i++;            continue;        }        builder.append(pipeline.charAt(i));    }    splits.add(builder.toString());    return splits;}For fun here's a more complicated output:static <T> Function<Stream<T>, Stream<T>> reverse() {    return s -> {        final Object[] sArray = s.toArray();        return IntStream.rangeClosed(1, sArray.length)                .mapToObj(i -> (T) sArray[sArray.length - i]);    };}static <T> Consumer<Stream<T>> forEach(final Consumer<T> f) {    return s -> s.forEach(f);}static <T> Function<Stream<T>, Stream<T>> concat(final Stream<T> right) {    return left -> Stream.concat(left, right);}...final String test =    ints                                      \n +        |> stream()                           \n +        |> concat(ints                        \n +            |> stream()                       \n +            |> reverse())                     \n +        |> forEach(System.out::println)       \n;System.out.print(buildPipeline(test));Which prints final List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5);pipe(pipe(pipe(ints, stream()), concat(pipe(pipe(ints, stream()), reverse()))), forEach(System.out::println))Which when run will output:1 2 3 4 5 5 4 3 2 1
Creating a pipeline operator in Java
java;functional programming;generics;stream;lambda
This starts getting messy quickly, preventing authors from writing readable one-line expressions.Based on this statement, I don't get, how you want to use your code. You definitely don't want to start programming in a string (see evil eval), nor is it getting less messy with the nested pipe() calls.What about a more java-approach. Just wrap the stream and add your methods. With some formatting, your are getting pretty close to your layout.import java.util.Arrays;import java.util.Collections;import java.util.List;import java.util.function.Consumer;import java.util.stream.Collectors;import java.util.stream.Stream;public class Pipe<O>{    public static void main(String... args)    {        final List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5);        Pipe.of(ints)//            .concat(//                Pipe.of(ints)//                    .reverse()//            )//            .forEach(System.out::println);    }    private static <O> Pipe<O> of(final List<O> objects)    {        return new Pipe<>(objects);    }    private final Stream<O> stream;    public Pipe(final List<O> objects)    {        this(objects.stream());    }    private Pipe(final Stream<O> stream)    {        this.stream = stream;    }    public Pipe<O> concat(Pipe<O> other)    {        return new Pipe<>(Stream.concat(stream, other.stream));    }    public Pipe<O> reverse()    {        final List<O> list = stream.collect(Collectors.toList());        Collections.reverse(list);        return new Pipe<>(list);    }    private void forEach(Consumer<O> action)    {        stream.forEach(action);    }}Maybe you could even implement Stream, but I didn't checked this in detail.