Forums Register Login
Functional Programming: what am I missing?

I have lately asked advice on a class I have created and got a lot and amazing advice which I have applied mostly. I have reached the part in which Predicates was advised and to read on Functional Programming to understand them. I have been reading the following articles on it but I literally understood nothing. Can someone please explain to a noob here what if I am missing?

Thank you

Let's move you to our functional forum.

I would suggest you look for a different tutorial; that tutorial isn't at all good. It is reasonably easy to understand if you already know some functional programming, but it is no use to a newbie. I think you will find P-YS' book more useful; start reading on the Manning website.
(0 likes, 2 cows)
I'm not sure where to start, since "I literally understood nothing" is quite vague, but totally understandable. I agree with Campbell's assessment on the tutorial: it does seem to go much into the motivation (why you should care about any of the points listed), instead assuming that you already kind of know what Functional Programming is all about. I'll try and walk through the first of those three articles and rephrase to try and shed some more light on the subject. If you have more specific questions though, that would be really helpful for us to understand what you're feeling unsure about and better target our answers.

I'm going to stay away from the examples given in the article. They seem a little weird to me, and at least the first one is not quite the right way to use the Stream API. Instead, I'll try and stick to concepts and only show (sort of) the Java APIs once the concepts have been introduced.

The three main points listed by the article really fall under two important ideas: functions are first class, and so can be passed around, even to other functions; and functions should be "pure", depending only on their inputs to generate their outputs. Together, these two ideas form a programming paradigm where the function is king and mutation is limited. This is where all the hype comes from: modern development likely needs to take advantage of multiple cores, which means a certain degree of parallel processing. Functions that don't mutate and have predictable outputs based on their inputs are much easier to reason about (and debug!) than objects and methods that have mutable internal state.

I'll try to showcase how functional programming can help make it easier to reason about a problem with a (hopefully not too) simple example: we have a list of people's ages and we want to calculate the youngest adult age (>= 18).

Starting with a more imperative approach, we might try something like:

There are a few interesting things going on here. First, youngestAdultAge is (potentially) assigned to multiple times. Here we don't really have a problem, aside from the potential for mistakenly reassigning the variable, but imagine if we had a huge number of ages to process: it wouldn't be obvious how we might go about parallelizing this implementation without the multiple threads clobbering each other by writing to the same variable. Second, the logic is a little spread out (capturing the youngest age is spread over the declaration, half of the if and the assignment). And third, we'd have to copy/paste the whole function with a few changes if we wanted to find, for example, the oldest child.

So let's consider what we might need here if we want to instead approach the problem in a more functional way. First, let's write some functions: one to determine if a given age was >= 18, and another to determine the minimum of a two ages:

The first function is what we would call a Predicate: it's a function that, given some input, makes a decision about that input and returns either true or false. There really isn't any difference between a "predicate" and a "function"; a predicate is just a function that returns a Boolean. Typically, predicates are used to select elements out of a sequence (i.e. decide which elements should be processed and which should be ignored).

This is where the notion of "higher-order" functions (functions that take functions) comes in: imagine that our sequence of ages has some method on it, let's call it filter, that takes a Predicate and applies it to each element of the sequence. Selecting all the adult ages would then look something like (I'm assuming you're familiar with lambda syntax):

So now we have all the adult ages, we want to find the youngest (minimum). We have our function for that, but how would we use it? What we'd like to do is take that filtered sequence of ages and reduce it to a single value: the youngest age. Image that our sequence of ages has another method on it, let's call it reduce, that takes a function to apply over and over to the sequence of ages until the result is a single value. Finding the youngest adult would then look something like:

The reduction may require a little more explanation than the filter. I've called the two arguments to the function youngest and age because the first is there to track the youngest age in the same way as the youngestAdultAge in the imperative solution, and the second is the age we are currently comparing against the youngest. To visualize what's going on, consider this sequence of ages: [32, 22, 40]. The reduction does the following:

For completeness, we'll use a two-argument variant of reduce that takes an identity as its first argument. This is an identity in the mathematical sense: including it in the reduction should not cause any change to the calculation. For addition, the identity is 0; for multiplication, the identity is 1; for our current example, we've already seen the identity in the imperative implementation: Integer.MAX_VALUE. Here are the two implementations together for comparison:

You can see here that the functional implementation is much closer to the problem statement than the imperative one. Also, because it takes advantage of higher-order functions, it's trivial to change what we're looking for. Going with the earlier example of oldest child, we would just have to change what we plug in:

I hope that helps!
Thank you Jason, I have the basic idea I will attempt the methods i was recommended to do using FP and will ask again if I have any questions
Go back to the Manning link I gave you and read that chapter. The principal idea of FP is probably that you are not allowed any side‑effects.

You can reduce the λs in JB's code with method references. I shall leave that to YI as an exercise.

Campbell Ritchie wrote:Go back to the Manning link I gave you and read that chapter. The principal idea of FP is probably that you are not allowed any side‑effects.

You can reduce the λs in JB's code with method references. I shall leave that to YI as an exercise.

Will do cheers

Jason Bullers wrote: ...

+cow for a wonderful explanation. I am learning FP as well and I found your example quite useful.

Taking your example further, is this a good way to write it ?
Thanks for the cows

Salvin: I guess you're wondering what I would use in the Stream API to implement this?

I'd prefer IntStream to avoid unnecessary boxing and unboxing of ints and probably use the specialized min method instead of the more general reduce. The only other choice is what to do about the case of an empty Stream. That depends on usage: a "marker" like -1 may be sufficient for a method that's an implementation detail, but if it's part of a public API, I'd prefer to just send the Optional back to the caller and let them handle it as they like. So I'd do something like:
(0 likes, 1 cow)

salvin francis wrote:. . .

It is worth going through that code for the types of each line. To start: there are different indentation conventions. Ken Kousen says to align each dot for each intermediate operation in a vertical column as above (I used your code plus an extra space), and Urma Fusco and Mycroft align all the dots, as you did after the terminal operation in line 4. So let's look at the lines.
  • 2: Takes the int[] and makes an ordinary (sequential) IntStream handling all its elements.
  • 3: IntStream#filter() is an intermediate operation reducing the cardinality of the stream by only retaining those elements matching the predicate in the λ, so only ages ≥ 18 are retained. I am 99% sure you can replace that λ with a method reference Something::isAdult. Returns a second IntStream
  • 4: IntStream#reduce() is a terminal operation returning an OptionalInt, created by finding the value whih is returned from the Integer#min method. You would probably get the same effect from Math::min. The reason for returning an optional is that there is the possibility that no values pass the filter and you are here presented with an empty Stream.
  • 6: The orElse method takes the int from the optional and returns it: if the optional was empty, however, it returns the argument −1.
  • Salvin's code doesn't involve any boxing or unboxing: the Arrays#stream(int[]) method returns an IntStream directly. The IntStream#min() method also returns an OptionalInt. It goes to show you there are always several ways to do the same thing.
    That's an amazing explanation Campbell,

    Campbell Ritchie wrote:3: .... I am 99% sure you can replace that λ with a method reference Something::isAdult. Returns a second IntStream...

    Yes, a method reference works too. I verified.

    Jason Bullers wrote:... but if it's part of a public API, I'd prefer to just send the Optional back to the caller and let them handle it as they like...

    That's sounds good too. I hope OP has learnt a few good things from this post. I know I have !

    salvin francis wrote:... That's sounds good too. I hope OP has learnt a few good things from this post. I know I have !

    I have Just need to practice this since I still am unable to benefit from it
    Thank you for the cow whoever it was.


    Among the problems I'm facing with the various introductions to Function Programming is that precisely what it is so often centers around:

    1. You can do this! (With example that supposedly saves time).
    2. You can say what you want done, not how to do it.  (A clumsy attempt at declarative definitions IMO because "what you want done" requires the imperative of how to do it).

    ...when before all of that there should be the emphasis on this:

    1. You focus on the removing of state.

    It took just one posting from Robert C. Martin, (AKA "Uncle Bob"), for me to realize this, and prior to that the benefits to avoiding state were taking a distinct back seat.

    In his various follow up articles he pointed out quite simply:

    1. The speed of light, and hence slightly lesser speed of electron propagation, is finally truly the limiting problem in classic CPU design speed.  This is why the curve of ever increasing clock speeds has flatlined.
    2. We can't easily shrink things either to gain speed any longer because the conduit widths are already measured in numbers of atoms.
    3. Currently, the only thing truly on the horizon (and no, I agree with him that Quantum computing is still not close to being proven as real, despite the flurry of articles that mistakenly state what the qubit is doing), is to add cores.
    3. Java engineer teams can barely manage getting two threads to cooperate when there is state change involved, even with help from concurrent facilities.
    4. And then he asked an important question: Are you ready for 32 cores?  1024 cores?  65536 cores?

    This brought me right back to my early exposure to Lisp where there were no loop constructs other than recursive calls, and the reason behind it all just clicked.  I had a model that makes sense.

    So now I'm in this far more comfortable learning position (for me): FP is a win not because you can now do this instead of that and make it less wordy code (the opening descriptions from one page after another).  It's a win because your focus is on avoiding state, and by avoiding state you can avoid all the pitfalls of state, the primary one being that you can't easily scale horizontally to multiple threads/cores.

    Starting from there, I can now finally see the wisdom in the Lambda expressions emphasis in Java 8, and have something to focus on.

    This thread has been viewed 1137 times.

    All times above are in ranch (not your local) time.
    The current ranch time is
    Oct 20, 2018 08:33:08.