HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Creating a pipeline operator in Java

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
creatingpipelineoperatorjava

Problem

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 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 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 fun

Solution

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
{
    public static void main(String... args)
    {
        final List ints = Arrays.asList(1, 2, 3, 4, 5);
        Pipe.of(ints)//
            .concat(//
                Pipe.of(ints)//
                    .reverse()//
            )//
            .forEach(System.out::println);
    }

    private static  Pipe of(final List objects)
    {
        return new Pipe<>(objects);
    }

    private final Stream stream;

    public Pipe(final List objects)
    {
        this(objects.stream());
    }

    private Pipe(final Stream stream)
    {
        this.stream = stream;
    }

    public Pipe concat(Pipe other)
    {
        return new Pipe<>(Stream.concat(stream, other.stream));
    }

    public Pipe reverse()
    {
        final List list = stream.collect(Collectors.toList());
        Collections.reverse(list);
        return new Pipe<>(list);
    }

    private void forEach(Consumer action)
    {
        stream.forEach(action);
    }
}


Maybe you could even implement Stream, but I didn't checked this in detail.

Code Snippets

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);
    }
}

Context

StackExchange Code Review Q#149775, answer score: 5

Revisions (0)

No revisions yet.