• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Rob Spoor
  • Tim Cooke
  • Junilu Lacar
Sheriffs:
  • Henry Wong
  • Liutauras Vilda
  • Jeanne Boyarsky
Saloon Keepers:
  • Jesse Silverman
  • Tim Holloway
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Mikalai Zaikin
  • Piet Souris

Java Usage of Consumer function

 
Ranch Hand
Posts: 101
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
HI,

I want to create a simple class which has a single instance variable int count.
I want to be able to add/decrease the value of count. But i dont want to have methods increment/decrement. i want a single method which does the same by taking a lambda


if you see the below code, when i use a FUnction it works.
however if i use a consumer , i am not able to do it using a consumer.

i get this error. The left-hand side of an assignment must be a variable and The method changeState1(Function<StateCount,Integer>) is ambiguous for the type StateCount




 
Stanley Walker
Ranch Hand
Posts: 101
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
essentially, i am not able to figure out why FUnction works and consumer does not
 
Bartender
Posts: 4633
182
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
hi Stanley,

it is the expresson 'c -> s.getCount() += 1' that is invalid. In fact, it boils down to: s.getCount() = s.getCount() + 1, which is invalid.

If you add the method

and make the Consumer:

then all is well.
 
Stanley Walker
Ranch Hand
Posts: 101
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:hi Stanley,

it is the expresson 'c -> s.getCount() += 1' that is invalid. In fact, it boils down to: s.getCount() = s.getCount() + 1, which is invalid.

If you add the method

and make the Consumer:

then all is well.



Hi Piet,

Thank you for the response.

I understand  s.getCount() = s.getCount() + 1 is an invalid statement

But why does it work when i use a Function

check here


the above code works. How come consumer it doesnt work

like below

 
Piet Souris
Bartender
Posts: 4633
182
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
hi Stanley,

as said: it is not a question of whether a Function works and a Consumer doesn't, but the question what is a legal Function and what is a legal Consumer.

I must say: your code, although nothing wrong with it, is not very transparant. To make the error a little easier to spot, let's make a separate Function and a separate Consumer. If you get an error in one of these, then you know that the problem is not in the 's.changeState(2)' part of the code,

So here goes:

So, when we apply this function to some StateCount c, then the return value is c.count + 1.
On its own, c.count + 1 means nothing, you get a compiler error here, stating that this value should be assigned to a variable. Now have a look at the method 'stateChanged':

You invoke it with:

So, in effect, what you do is saying:

and as you can see, this is perfectly legal code.

Now for the Consumer part. Again, let's check if the Consumer as you defined it is correct:

As you will notice, this gives a compiler error, stating that an int (the result of the right part) cannot be converted to void, as a Consumer must be.
So, let's try a variant:

Now we return nothing, but as before, we get the error that the right hand part is not a statement and should be assigned to a variable.
Well, let's do that, but the way I do it makes it necessary to make the variable 'count' available to other classes as well: make it a public variable (that is bad practice, never do that in your code, but it is just for demonstrations sake)

And that is perfectly legal as well.

So, with the Function sToi and Consumer consumer defined (and legal), we can invoke them, either as you invoke them,but also as follows:

As said, having the separate sToi and consumer makes it just a tad easier to see what is going on.

Looking back, I must admit that this is actually not an easy subject at all! It is a subject that takes a lot of practising, and a lot of trying until you get it right! But the reward of putting all that effort is there: it is big fun working with all of these Consumers, Functions, Predicates and similar. Of course, if you have additional questions, just come up with them.
 
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Additionally, your usage of the Function and Consumer interfaces are smelly. I can't quite put my finger on it but it just seems like there's way too much unnecessary intimacy between the StateCount class and the lambdas. The lambdas having a reference to StateCount does not look right to me. And Consumer seems like the opposite of what you should use. That is, it seems to me a Producer Supplier is more appropriate for what you want to do. I'll try to come up with an example to show something I think is more sensible.
 
Piet Souris
Bartender
Posts: 4633
182
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Beware of making the same mistake as I sometimes make. In topics about statistics, focussing on creating a decent data collection, for some statistical investigations, forgetting that the topic is about practising some data structures and not so much about statistics.

Supplying a Consumer to an object, to change one of the fields, is not what you normally encounter, but it certainly makes for some nice practice.
 
Stanley Walker
Ranch Hand
Posts: 101
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:Additionally, your usage of the Function and Consumer interfaces are smelly. I can't quite put my finger on it but it just seems like there's way too much unnecessary intimacy between the StateCount class and the lambdas. The lambdas having a reference to StateCount does not look right to me. And Consumer seems like the opposite of what you should use. That is, it seems to me a Producer Supplier is more appropriate for what you want to do. I'll try to come up with an example to show something I think is more sensible.



Hi Junilu,

I promise you, i was just trying out different ways to fail. to be honest, thats the only way i have learnt lambdas as much as  i have(which is not too much, i must confess)
i keep trying out weird stuff with functions api and this particular piece confused me, since one works and the other doesnt. and compiler errors are not that descriptive either

Can i ask you a question - why do you say  

Junilu Lacar wrote:between the StateCount class and the lambdas


Is it because the lambdas passed in is changing the state of the class??
 
Stanley Walker
Ranch Hand
Posts: 101
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:hi Stanley,

as said: it is not a question of whether a Function works and a Consumer doesn't, but the question what is a legal Function and what is a legal Consumer.

I must say: your code, although nothing wrong with it, is not very transparant. To make the error a little easier to spot, let's make a separate Function and a separate Consumer. If you get an error in one of these, then you know that the problem is not in the 's.changeState(2)' part of the code,

So here goes:

So, when we apply this function to some StateCount c, then the return value is c.count + 1.
On its own, c.count + 1 means nothing, you get a compiler error here, stating that this value should be assigned to a variable. Now have a look at the method 'stateChanged':

You invoke it with:

So, in effect, what you do is saying:

and as you can see, this is perfectly legal code.

Now for the Consumer part. Again, let's check if the Consumer as you defined it is correct:

As you will notice, this gives a compiler error, stating that an int (the result of the right part) cannot be converted to void, as a Consumer must be.
So, let's try a variant:

Now we return nothing, but as before, we get the error that the right hand part is not a statement and should be assigned to a variable.
Well, let's do that, but the way I do it makes it necessary to make the variable 'count' available to other classes as well: make it a public variable (that is bad practice, never do that in your code, but it is just for demonstrations sake)

And that is perfectly legal as well.

So, with the Function sToi and Consumer consumer defined (and legal), we can invoke them, either as you invoke them,but also as follows:

As said, having the separate sToi and consumer makes it just a tad easier to see what is going on.

Looking back, I must admit that this is actually not an easy subject at all! It is a subject that takes a lot of practising, and a lot of trying until you get it right! But the reward of putting all that effort is there: it is big fun working with all of these Consumers, Functions, Predicates and similar. Of course, if you have additional questions, just come up with them.




THank you Piet, i totally appreciate the pain you took to explain the problem to me. And i must say, i do understand now why one works and the other doesnt.

Appreciate your help
 
Piet Souris
Bartender
Posts: 4633
182
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You're welcome!
 
Junilu Lacar
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Piet,

I was addressing OP. I actually agree with practically all of the things that you said in your previous replies.
 
Junilu Lacar
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sorry for this late response. It's been a pretty busy weekend.

Let me try to clarify some of what I said previously with examples.

OP wrote:...trying out different ways to fail


That's a perfectly fine way to learn. Yoda even said that "The greatest teacher, failure is."

Piet may have already gone over this but I'll reiterate: Your attempt to use a Consumer lambda failed because of the expression c.getCount() += 1.  The += operator is a compound operation which combines an addition operation with an assignment operation. It's illegal to try to assign a value to a method call, which is what c.getCount() is, hence the error message that says "the left hand side of an assignment must be a variable".

OP wrote:why do you say "between the StateCount class and the lambdas"


I think it's because I didn't see any reason for the lambda to know about StateCount. I got a sense that the code you wrote was going around the bush to do something that could be accomplished in a more straightforward manner. If I really wanted to use lambdas to do increment/decrement, I would have done something like this:


You can try this code out yourself here: https://repl.it/@jlacar/SupplierLambdasExample

Study that example for a while and come back if you have any questions.
 
Greenhorn
Posts: 23
1
IBM DB2 Netbeans IDE Chrome
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Stanley,

I think Jose Paumard's video, Free your Lambdas would help you understand lambdas better, It helped me.

 
Junilu Lacar
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think my unease with using a Consumer in this case is that I think of a "consumer" as something that does something with whatever it is passed, not something to, which seems to be what you want to do when you desire to change the internal state of the StateCount object using a lambda. The typical example of a Consumer usually just prints out something using the thing it was passed. Granted, that's probably the easiest way to illustrate how a Consumer works.

My mental model is like this:

A Consumer gets a value from its argument, then uses that value to do something

A Supplier gives its argument a value that the argument then uses to do something

Your code went against this mental model, so it was smelly to me. Let's say I ignore my mental model and went with what you were trying to do instead. I might have tried this:


I would have to add a couple of overloaded change() methods:

See the change2() method here: https://repl.it/@jlacar/SupplierLambdasExample
 
Junilu Lacar
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I should point out that while the second example I gave above that uses a Consumer to change the state of the UpDown object via the change(int) method works, the code is still problematic to me.

The semantics of the name "consumer" does not match what the code in the lambda actually does: it is giving a value to its parameter, ud, via the change(int) method. So the "consumer" wasn't actually consuming anything. Instead, it was causing its argument to mutate. That kind of mismatch between what the name in the code says and what the code actually does is what wreaks havoc with my cognitive abilities. My brain doesn't like being made to jump cognitive hoops like that, so it tells me that the code is smelly.

On the other hand, the Function lambda you used doesn't have that problem because what your lambda does lines up with my mental model of what a "function" would do: it uses its argument to calculate a value, then returns the result of that calculation. The client is free to do whatever it pleases with that result.
 
Junilu Lacar
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm going to experiment with Consumer some more. I have a nugget of an idea that I might be able to use it to put a twist on a Conway's Game of Life implementation where I'm not using a normal "grid" structure to track generations and populations but rather some kind of sparse matrix that I can stream. The idea is to be able to write something like this:

Consumer lambdas will come into play as encapsulations of the rules for survival from one generation to the next:
- a live cell with 2 or 3 neighbors survive
- a live cell with less than 2 neighbors dies of loneliness
- a live cell with more than 3 neighbors dies of overcrowding
- an empty cell with exactly 3 neighbors becomes a live cell.

Each Consumer will "consume" the current position in the board that is being checked and could potentially produce a side-effect that is manifested in the next generation object. I think this is consistent with the API docs statement about Consumer:

Java API Documentation for Consumer wrote:Unlike most other functional interfaces, Consumer is expected to operate via side-effects.

 
Stanley Walker
Ranch Hand
Posts: 101
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
hi Junilu

thank you so much for your responses.
this is realy helpful

 
Piet Souris
Bartender
Posts: 4633
182
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:Additionally, your usage of the Function and Consumer interfaces are smelly. (...)


Yes, I had that feeling too, and your replies made me think about it.

We have Consumer<T>, that represents some action that we can perform on an object of type T. But what could that be? Well, only those actions that are available to the outside world, i.e. non-private fields and methods (often public). If we look at the priginal code, then we see that both the Function and the Consumer are acting on the private field 'count'. Since this field is private, how is the putside world to know what Function or Consumer to supply? One possibility was to make that field public, but a much better way was to create the Function as Junilu described (the change(i) method). But having this method available, why would you use a Consumer instead of invoking the method directly? I can only think of using it in a Collection.forEach, that requires a Consumer.

Is Consumer<T> really the most confusing of the Functional Interfaces? Now I'm confused...  

But it is a perfect exercise, illustrating some very nice problems! OP, enjoy a cow (Junilu, you get one tomorrow!)
 
Junilu Lacar
Marshal
Posts: 16594
277
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks, Piet. Your participation in the discussion is very helpful in helping me organize and express my thoughts around these issues.

My approach to design and detecting smells is really anchored on clarity and expressiveness. Not violating the Principle of Least Astonishment also plays into my thought process. Tell, Don't Ask also keeps me on the OO path and aligned with GRASP design principles.

When I see code that seems unclear or confusing to me, the first thing I do is try to understand the intent. I will mostly ignore the implementation details. When I understand the intent, I ask myself "What would the code look like that clearly expresses that intent?"  Most likely, that code does NOT contain a lot of implementation details. I tend to defer thinking about implementation details until I have a good feeling about what the high-level code would look like.

That's kind of what happens when I write some "theoretical" code like:


That code states my goal or intent. Then I drill into the populateFrom() method and try to think of how to state my intent in there. I just keep drilling down until I am at a  level where I can no longer avoid writing implementation detail code.
 
Piet Souris
Bartender
Posts: 4633
182
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
@Junilu

as promised!
 
Don't get me started about those stupid light bulbs.
reply
    Bookmark Topic Watch Topic
  • New Topic