I;m trying to write code that does that following as a map() step in a stream pipeline. In other words, it goes through a bunch of strings and removes certain sequences of letters form each. This works, but I don't like the loop. Is there anything I can do to be more functional here?
And yes, I'm aware this code needs a refactoring for performance so it doesn't read the file multiple times.
Here is a functional solution. Note that this is using only Java standard library classes, which considerably limit possibilities. The most important problem is about Optional which doesn't carry the error in case data is missing. In my book (Functional Programming in Java), I show how to implement much more powerful data structures. Also note that the removeForCustomLogic is in curried form, but you can use a BiFunction if you prefer. I have included a main method to show how it is intended to be used. Of course, using the Java HashSet this way is not functional at all, but it is supposed to be replaced by a call to the getPrefixes method.
I plan to write a chapter in my book about translating imperative programs into functional style. May I use your example?
I think you can use a sequential reduction, although it technically breaks the contract of reduce(), which requires an associative operator.
Please note that without the call to sequential(), there is a high chance of failure because the reduction is not associative. That is, ((s-p1)-p2) has a different result than (s-(p1-p2)).
Why not write a custom UnaryOperator? I would just keep this code procedural, it's much easier to read, and it guarantees that the operation is performed sequentially. Also, functional code doesn't play nice with I/O. I don't recommend combining the two.
I think this problem is a fantastic example why maybe it was a mistake to intertwine parallelization and Streams so much. The rationale is that solving a problem in parallel or sequentially should be an implementation detail, hidden by our beautiful higher order functions:
java.util.stream wrote:Except for operations identified as explicitly nondeterministic, such as findAny(), whether a stream executes sequentially or in parallel should not change the result of the computation.
The problem is that this implementation detail leaves methods like reduce() theoretically useless in a lot of cases if they don't make guarantees on the order of reduction. Either the API should have made hard guarantees for sequential streams, or they should have added a method such as reduceLeft() or foldLeft().
I don't think you need to call sequential because stream() returns a sequential stream. But of course, this is a dangerous trick. I you later forget about it and make the stream parallel, It might give a totally unexpected result.
Beside this, I agree that Java 8 streams are mostly useless for functional programming! The same is true about Optional, since it can only represent data for which absence is not an error. Here is how the same example looks like using the library developed in my book:
Of course, any functional library may be used instead.