• Post Reply Bookmark Topic Watch Topic
  • New Topic

Functional Programming  RSS feed

 
Yosuf Ibrahim
Ranch Hand
Posts: 128
4
Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

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

Links:
https://flyingbytes.github.io/programming/java8/functional/part0/2017/01/16/Java8-Part0.html
https://flyingbytes.github.io/programming/java8/functional/part1/2017/01/23/Java8-Part1.html
https://flyingbytes.github.io/programming/java8/functional/part2/2017/02/04/Java8-Part2.html
 
Campbell Ritchie
Marshal
Posts: 56220
171
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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.
 
Jason Bullers
Ranch Hand
Posts: 115
11
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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!
 
Yosuf Ibrahim
Ranch Hand
Posts: 128
4
Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
 
Campbell Ritchie
Marshal
Posts: 56220
171
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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.
 
Yosuf Ibrahim
Ranch Hand
Posts: 128
4
Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
 
salvin francis
Saloon Keeper
Posts: 1644
37
Eclipse IDE Google Web Toolkit Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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 ?
 
Jason Bullers
Ranch Hand
Posts: 115
11
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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:
 
Campbell Ritchie
Marshal
Posts: 56220
171
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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.
     
    salvin francis
    Saloon Keeper
    Posts: 1644
    37
    Eclipse IDE Google Web Toolkit Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    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.
     
    salvin francis
    Saloon Keeper
    Posts: 1644
    37
    Eclipse IDE Google Web Toolkit Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    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 !
     
    Yosuf Ibrahim
    Ranch Hand
    Posts: 128
    4
    Eclipse IDE Java Windows
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    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
     
    Campbell Ritchie
    Marshal
    Posts: 56220
    171
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Thank you for the cow whoever it was.
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!