Win a copy of Classic Computer Science Problems in Swift this week in the iOS forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Question to Pierre-Yves Saumont  RSS feed

 
Marshal
Posts: 5635
390
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi once again, Pierre-Yves Saumont,

Probably month or so ago I just used to have a look at others functional style code attempts, but was quite reluctant to write such code myself, even though we have Java 8 for quite a while now, besides, at work we use Java 8 too, so I do have facilities for that.

However, recently I took a course in one of the sites about the cryptocurrencies (well, haven't finished yet) and they do have 3 assignments. So I decided to attempt solving them with as much as possible functional style.

And to my big surprise, very first assignment I managed to solve with methods using only the functional style approach.

1. So my question I guess is, do you think one should use functional approach whenever possible? Or let's say only in the places where code is more concise/maybe more clear (to be honest it is almost always more concise), but in this case you would end up with mixed style code.

Now, since I'm working with team, probably would make mostly sense if all team members would feel comfortable with such approach, otherwise communication could get slightly complicated. But then again, looking to some approaches, I'd say some of solutions can't be more clear than using functional approach, i.e.:
Regular approach would look similar to this:

2. Do you think team should have consensus first on the preferred approach? What arguments/metrics we should bring on the table when we discuss and trying to define preferred approach?

3. As a controversial topic, there is always performance mentioned. Doing some research, I found cases where streams let's say are faster on bigger inputs, while on smaller inputs regular approach were significantly faster. Do you attempt solving problems in both approaches then doing tests and then decide to which approach you're going to stick with?


Thank you for your answers/thoughts. I know, there are quite a few questions. Please answer based on your availability.
 
Marshal
Posts: 58829
179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As I said yesterday, there are still people who are scared of Streams because they are difficult to write and easy to get wrong, and they know that loops are easy to write and hard to get wrong.
As I also said yesterday, they know it all wrong.
I think the trick is to start small. The example you showed yesterday, with IntStream.rangeClosed, is a good starting place. Go through your Stream code bit by bit, the way you would with beginners. You will find the Stream code easier to write and if you get it wrong, you are more likely to get a compiler error than a runtime error or an incorrect result. Also indent the code differently. I learnt Stream indentation from Urma Fusco and Mycroft's book, and they seem to indent it so all the dots align vertically; Ken Kousen aligns the dots vertically before any operation creating a new Stream (=an intermediate operation). So your code would look like this:-or like this:-Now that is divided into parts (I think I prefer the first indentation type), it becomes easy to read. Read it out aloud and then read it as

Return a stream made from the Arrays class' method from possibleTxs, filtered by the isValidTx method and collected with the Collectors toSet collector.

Then try reading the loop code similarly.
Run the code on your IDE; hover your mouse over the words stream and filter and their return values will appear in a popup (at least they do on Eclipse). You can then read that you have a Stream<Transaction> (twice) and collect will show you a Set<Transaction>. Avoid putting the mouse on toSet because “Collector<T,?,Set<T>>” might scare your readers .
 
Ranch Hand
Posts: 65
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi

I wanted to ask if you have any experience about adding  functional style programming to existing java projects? Any thoughts? For me functional programming is completely different mindset, and honestly I cannot imagine how this could be introduced into project with thousands line of code. Of course, in case of microservice architecture it's easy, as each microservice can be written independently and can be written even in different language. In case of some 'core' service it might be hard. Do you have any experience with such scenarios? Can you share some good practices?
 
Author
Rancher
Posts: 158
31
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Liutauras,

Some answers were posted while I was writing mine, so some ideas might be duplicated!

Your first question is  legibility, but it also concerns ease of debugging. Your example could be rewritten as:



This generally helps imperative programmers to understand what's happening because it looks like a sequence of operations.

Another benefit is that it is easier to put breakpoints on each separated part. Although a smart debugger will allow you to select on which part you want to put the breakpoint even when the code is on a single line, you might have trouble to later remember which part of the line the breakpoint applies to.

The main benefit of the functional implementation is that the code only shows what is done, and not how it is done. The imperative way exposes the loop as a mean to iterate over a collection, and the conditional instruction as a mean to test for a condition. It is easy to mess with these details. In the functional approach, these details are abstracted, so you can't mess with them. By the way, the same kind of abstraction was use to switch from indexed loop to the "for each" version. Functional programming pushes abstraction much further.

In the same way, this approach does not expose the Set, and it might also produce an immutable set, which of course makes the code safer. It is safer than good imperative code, but moreover, it is safer that bad imperative code that is so easy to produce, such as:


This example is a bit contrived, but you see the point. With loops, one can always close over some variable in enclosing scope, even sometimes inadvertently. The functional way protects us against this.


Regarding your second question, it is, of course, better when a team agrees upon a preferred approach. But it is not always necessary. Many functional techniques may be used without breaking so-called "best practices". In fact, we should consider different cases:

- Several programmers working on the same code at the same time should probably agree about what type of code they write.

- Working alone on some code means your code might eventually be maintained by others. I such case, nonstandard techniques should be thoroughly documented to ensure the that the maintainers will understand your code. (Or to ensure that you will understand your code some years later!)

- Working on a library that is used by other programmers in your team is a different case. For the implementation, the rules above apply. But for the API, it is a different problem. You have to use the types that your users are expecting. Returning unevaluated results, for example, would probably not be an option. In Java, this would correspond to returning a Supplier<A> instead of an A, or returning a Stream instead of a List. (Returning streams is probably not a good idea in any case, but for other reasons!). Of course, returning unevaluated effects would be even worse since your user would generally not expect your code to return anything, but to evaluate the intended effect.

Regarding the third question, my personal choice is to always use a functional implementation. I do not care about it being the fastest possible implementation. It just has to be fast enough. Unfortunately, how fast the code has to be is rarely part of the specification. If the code happens to be too slow, I consider switching to an imperative implementation as an optimisation, but I would as often as possible wrap it in a functional interface and avoid making the imperative aspects visible from the user.
 
Pierre-Yves Saumont
Author
Rancher
Posts: 158
31
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Marek Krokosinski

As a library designer, I have no problem using functional implementation although my libraries are used by imperative programmers. You're right that functional programming is a different way of thinking. But introducing functional programming in a project with thousands (or even millions) lines of code is not a problem. Or if it is a problem, it is because this project is badly written. A thousand lines of code could be one hundred ten lines long methods, or it could be one method with one thousand lines of code. In the latter case, it might be difficult to introduce changes using functional techniques, but this is not related to functional programming. It is related to how badly written code is difficult to maintain. On the other hand, a method with a thousand lines of code is difficult to break into several smaller methods when it evaluates several effects and/or closes over many resources (especially mutable ones). This is, among other things, what functional programming avoids.

My advice would be to refactor the imperative code in order to:

- isolate effects into distinct methods (never have any effect evaluated while returning a value). And methods evaluating effects should apply the single responsibility principle.

- make all methods returning a value referentially transparent: no closing over references in outer scopes and no mutation of arguments.

- try making all instance variables final. Closing over a final variable is much less of a problem. Such variables may even be considered as implicit arguments. By the way, making them explicit arguments is generally a good idea.

- Abstract all that is possible in separate methods/functions.

With such refactoring, you will have a much more modular code composed of small methods. It is then much easier to rewrite method implementations using functional programming techniques (if necessary).

 
Liutauras Vilda
Marshal
Posts: 5635
390
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks a lot Campbell and Pierre-Yves for detailed explanations.

Those things make sense.

What Rob Spoor also mentioned the other day about indenting streams on separate lines rather than chaining as one-liner, that in case you get a runtime error/exception, it clearly tells you on which line it happened, so writing on separate lines is indeed beneficial.

Based on my own experience writing some functional code recently, I start liking it more and more. Probably just to kick it off not so easy. Gone back and checked my notes about using one of Lisp family languages (= Racket), that helps to pick it up easier functional Java too.

Now that Java 9 came, with offering more immutable data structures out of the box, it seems Java heading by yards towards functional paradigm.

Anyway, will be glad to read your book once get one. And that will happen regardless

Thank you (both of you) once again for detailed and ellaborate explanations.

 
Campbell Ritchie
Marshal
Posts: 58829
179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Is an immutable collection part of functional programming, or simply an extension of the principle of defensive copies? We used to have to write things like
return Collections.unmodifiableSet(validTx);
or
return Collections.unmodifiableSet(new HashSet(validTx));
in order to take defensive copies. Now you can create an immutable Set and simply return that. You can even have an immutable collection field the same way you would have a String as an immutable field:-
private final String name = "Pierre-Yves";
or
private final String name = KeyboardInputs.nextLine("Please enter name: ");
 
Liutauras Vilda
Marshal
Posts: 5635
390
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Is an immutable collection part of functional programming...?


Well, we might cannot relate directly as functional programming part, but functional programming fundamentally provokes us to write thread safe code, so, an immutable collection along with immutable objects in general seems to me fit very well with functional programming concept. Now, many probably would start saying about performance, but we know all, that people would rather buy a car which can speed up to 300 km/h than 250 km/h, even though speed limit in most countries is 130 km/h on a highway, unless you live in Germany probably.
 
Pierre-Yves Saumont
Author
Rancher
Posts: 158
31
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Immutable collections might be used for functional programming. However, what FP means is "immutable persistent data sharing collections". What this mean is that you don't have to make defensive copy. Adding an element to a stack, for example, would be made by linking this element to the head of the stack. Previous references to the stack would not be modified. Creating the  "new" stack would not involve any mutation. Here is an example of dropping one element from a stack and then adding five new ones:



The same effect could be obtained with ArrayList. ArrayList are created with a backing array that is much bigger than the list itself. So adding an element could be done by adding an element to the array and modifying a pointer to the end of the list. The same could be done for appending or deleting element in front of the list. Functional data structures (meaning immutable persistent data sharing structures) are simply more efficient because one do not have to make a copy when the array is full. The counterpart is that in place mutation (replacing an element in the middle of the list) is much more complex to implement. But much safer.

 
Campbell Ritchie
Marshal
Posts: 58829
179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!