This week's book giveaway is in the Jython/Python forum.
We're giving away four copies of Murach's Python Programming and have Michael Urban and Joel Murach on-line!
See this thread for details.
Win a copy of Murach's Python Programming this week in the Jython/Python forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Mastering Lambdas: Question on Functional Design Patterns  RSS feed

 
S G Ganesh
Author
Ranch Hand
Posts: 93
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Maurice,

My question is about design patterns using lambdas. [I am relatively new to functional programming]

GoF's 23 design patterns are useful for creating reusable object oriented designs. Likewise, are there design patterns documented for functional programming? How does Java's lambda functions support these design patterns? Does your book cover functional programming design patterns?

Thanks!
 
Jeanne Boyarsky
author & internet detective
Sheriff
Posts: 36406
454
Eclipse IDE Java VI Editor
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Aren't design patterns higher level? Lambdas use patterns (like immutability) of course. But I feel like lambdas are closer to idioms than patterns.
 
Jason Bullers
Ranch Hand
Posts: 111
8
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Functional programming has its own set of design patterns, and, more interestingly in my opinion, completely changes some of the design patterns that you may be familiar with from object oriented programming. There are at least a few patterns I can think of off the top of my head that exist to almost get around the inability to pass around blocks of code. For example, the Strategy Pattern looks something like this is classic OOP:


That's a lot to write, and now if we wanted to add another WorkStrategy, we'd have to create a new new class, or else define it inline as an anonymous inner class. Neither option is very elegant.

Compare with what is open to us with lambdas:

You can see that defining new strategies, even inline, is trivial now. We don't care about any of the boilerplate wrapper object, we just care about the strategy we want to execute.

Another interesting one that lambdas is a natural fit for is the Execute Around pattern (similar idea to the Template Method pattern):
 
Campbell Ritchie
Sheriff
Posts: 53760
127
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Nose to the grindstone?

That's d*rn nice code (lines 21-22 second block). Yer gonna need new lariats and ter employ some more cowpokes.
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jason Bullers wrote:Compare with what is open to us with lambdas:
...
WorkStrategy noseToTheGrindStone = () -> System.out.println("Working hard!");
...
You can see that defining new strategies, even inline, is trivial now.

I certainly can, and for someone like me, who was a Unix sysadmin for 15 years and therefore familiar with scripts and piping, it's a very "natural" way of looking at things.

However, I do find the notation somewhat cryptic. For example, the "()" in the assignment above - can you put "execute()" instead? If so, it makes perfect sense; otherwise not.

Also, further down you have a 'Function<String, String>' parameter that appears to be satisfied by a 'line -> line.toUpperCase()' lambda argument - and that just looks "odd" to me.

I know that it's probably just my unfamiliarity with it all though, so you can be sure I'll be reading about it a lot.

Winston
 
Campbell Ritchie
Sheriff
Posts: 53760
127
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What goes on the left of the -> is the “arguments” for the λ. If there is one “argument” you can write
i -> i * i
You can put round brackets round the argument if you so wish. For any other number of “arguments” you have to pass a comma‑separated list of “arguments”
(i, j) -> i + j
but you need the round brackets to delineate it. In the case of something like a Runnable, its run method takes no arguments so you have to pass an empty comma‑separated list of “arguments” which looks like this:-

No, the compiler can see that no better than you could, so you have to bracket that empty list:-
() -> System.out.println("Cracking the whip over the workers!")

At least I think that is right.
 
Campbell Ritchie
Sheriff
Posts: 53760
127
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:Function<String, String> . . .
That means it takes a String as its input and produces a String as its output.
s -> s.toUpperCase()
is a nice example. It can take “Winston” and returns “WINSTON”. That is a function from a String to a String.
(s) -> s.toUpperCase()
does exactly the same.
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:For any other number of “arguments” you have to pass a comma‑separated list of “arguments”
(i, j) -> i + j

Ah, OK. That makes sense.

What I don't quite follow though is how the assignment knows that they are arguments to the execute() method. I understand that the interface only has one; but is this a requirement?

Thanks for the pointers though.

Winston
 
Campbell Ritchie
Sheriff
Posts: 53760
127
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, it is a requirement that the interface have one abstract method. If it has, then it is called a functional interface. There is an annotation corresponding. Runnable is a functional interface. It has one method full stop. So if you have a λ anywhere that a Runnable is expected, the compiler will presume that the run() method is meant. Since run has an empty arguments list, you use empty round brackets in the λ.
SwingUtilities.invokeLater(() -> new MyGUI(...));

You have the arguments to the left of the -> and the method body to the right.

Comparator has one method. Now I know you will think Campbell has finally flipped, since Comparator has two methods. But the equals() method doesn't count. Because it is override‑equivalent to that inherited from Object, the compiler doesn't regard that as an abstract method and only considers the compare method. So if you pass a λ where a Comparator object is expected, the compiler will presume you are trying to implement the one unimplemented method
(person1, person2) -> person1.age() - person2.age()
So far, so good. That was the Java7 version of Comparator. Which was identical to that in JDK1.2.0 because you cannot change public interfaces.
It gets even more complicated when you consider the Java8 version of Comparator. That would appear now to have 17 methods. But only one of them is unimplemented. As you know you cannot add unimplemented methods to interfaces. So there is still one method which needs to be implemented.
15 already implemented methods (static/default) 1 method which doesn't need implementing because it is override‑equivalent to an Object class method, and one method which really does need implementing. So if you have a method which expects a Comparator instance as an argument, you can pass a λ. The compiler will “think”
The Compiler wrote:Now, Comparable has 17 methods but only one of them really has to be implemented because all the others already are, so I shall presume he means that unimplemented method, so I shall take the two things to the left of the -> as the arguments and the bit to the right of -> as the body of the compare() method.
If you have something like a List<Foo> and you try to sort it with a λ, the compiler will presume that the type of the Comparator is Foo.Becasue list is a List<Foo> the compiler will presume you are looking for a Comparator<> which corresponds to a Comparator<Foo>. In this case the hornswoggle method returns something which implements Comparable.
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Yes, it is a requirement that the interface have one abstract method.
...
the compiler will presume you are looking for a Comparator<> which corresponds to a Comparator<Foo>. In this case the hornswoggle method returns something which implements Comparable.

Oh, OK.Noow, it's getting clearer. Cheers for that. Have a cow.

Winston
 
Maurice Naftalin
Author
Greenhorn
Posts: 20
6
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Ganesh

That's an interesting question. The book doesn't cover design patterns and, to be honest, I haven't thought much about how lambdas will affect their use. But my first thought is this: it will be much, much easier to implement many OO design patterns – for example, Visitor almost disappears, and Command is just itself a lambda. But the implementation isn't all that important: the idea of design patterns is to provide a language for communicating design intentions between developers. The fact that it will be easier to implement them won't change their fundamental purpose.

That's just a preliminary thought – it would be interesting to write something more detailed about this question.

Regards
Maurice
 
Don't get me started about those stupid light bulbs.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!