mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-13 12:14:51 +00:00
docs: update currying
This commit is contained in:
1 parent
0c31312510
commit
6cbcf0fe4a
1 file changed
+53
-25
+53
-25
@@ -3,7 +3,10 @@ title: Currying
|
|||||||
category: Functional
|
category: Functional
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
|
- Code simplification
|
||||||
- Functional decomposition
|
- Functional decomposition
|
||||||
|
- Generic
|
||||||
|
- Immutable
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -18,7 +21,7 @@ Currying decomposes a function that takes multiple arguments into a sequence of
|
|||||||
|
|
||||||
Real-world example
|
Real-world example
|
||||||
|
|
||||||
> Consider a librarian who wants to populate their library with books. The librarian wants functions which can create books corresponding to specific genres and authors. Currying makes this possible by writing a curried book builder function and utilising partial application.
|
> Currying in programming can be compared to an assembly line in a factory. Imagine a car manufacturing process where each station on the assembly line performs a specific task, such as installing the engine, painting the car, and adding the wheels. Each station takes a partially completed car and performs a single operation before passing it to the next station. Similarly, in currying, a function that requires multiple arguments is broken down into a series of functions, each taking a single argument and returning another function until all arguments are provided. This step-by-step processing simplifies complex tasks by dividing them into manageable, sequential operations.
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
@@ -28,7 +31,9 @@ Wikipedia says
|
|||||||
|
|
||||||
> In mathematics and computer science, currying is the technique of translating a function that takes multiple arguments into a sequence of families of functions, each taking a single argument.
|
> In mathematics and computer science, currying is the technique of translating a function that takes multiple arguments into a sequence of families of functions, each taking a single argument.
|
||||||
|
|
||||||
Programmatic example
|
**Programmatic example**
|
||||||
|
|
||||||
|
Consider a librarian who wants to populate their library with books. The librarian wants functions which can create books corresponding to specific genres and authors. Currying makes this possible by writing a curried book builder function and utilising partial application.
|
||||||
|
|
||||||
We have a `Book` class and `Genre` enum.
|
We have a `Book` class and `Genre` enum.
|
||||||
|
|
||||||
@@ -50,7 +55,7 @@ public class Book {
|
|||||||
public enum Genre {
|
public enum Genre {
|
||||||
FANTASY,
|
FANTASY,
|
||||||
HORROR,
|
HORROR,
|
||||||
SCI_FI;
|
SCI_FI
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,15 +67,18 @@ Book createBook(Genre genre,String author,String title,LocalDate publicationDate
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
However, what if we only wanted to create books from the `FANTASY` genre? We could pass in the `FANTASY` parameter on each method call; however, this is repetitive. We could define a new method specifically for creating `FANTASY` books; however, it is infeasible to create a new method for each book genre. The solution is to create a curried function.
|
However, what if we only wanted to create books from the `FANTASY` genre? Passing the `FANTASY` parameter with each method call would be repetitive. Alternatively, we could define a new method specifically for creating `FANTASY` books, but it would be impractical to create a separate method for each genre. The solution is to use a curried function.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
/**
|
||||||
|
* Curried book builder/creator function.
|
||||||
|
*/
|
||||||
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
|
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
|
||||||
=genre
|
= bookGenre
|
||||||
->author
|
-> bookAuthor
|
||||||
->title
|
-> bookTitle
|
||||||
->publicationDate
|
-> bookPublicationDate
|
||||||
->new Book(genre,author,title,publicationDate);
|
-> new Book(bookGenre, bookAuthor, bookTitle, bookPublicationDate);
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the order of the parameters is important. `genre` must come before `author`, `author` must come before `title` and so on. We must be considerate of this when writing curried functions to take full advantage of partial application. Using the above function, we can define a new function `fantasyBookFunc`, to generate `FANTASY` books as follows:
|
Note that the order of the parameters is important. `genre` must come before `author`, `author` must come before `title` and so on. We must be considerate of this when writing curried functions to take full advantage of partial application. Using the above function, we can define a new function `fantasyBookFunc`, to generate `FANTASY` books as follows:
|
||||||
@@ -79,9 +87,14 @@ Note that the order of the parameters is important. `genre` must come before `au
|
|||||||
Function<String, Function<String, Function<LocalDate, Book>>> fantasyBookFunc = Book.book_creator.apply(Genre.FANTASY);
|
Function<String, Function<String, Function<LocalDate, Book>>> fantasyBookFunc = Book.book_creator.apply(Genre.FANTASY);
|
||||||
```
|
```
|
||||||
|
|
||||||
Unfortunately, the type signature of `BOOK_CREATOR` and `fantasyBookFunc` are difficult to read and understand. We can improve this by using the [builder pattern](https://java-design-patterns.com/patterns/builder/) and [functional interfaces](https://www.geeksforgeeks.org/functional-interfaces-java/#:~:text=A%20functional%20interface%20is%20an,any%20number%20of%20default%20methods).
|
Unfortunately, the type signature of `BOOK_CREATOR` and `fantasyBookFunc` are difficult to read and understand. We can improve this by using the [builder pattern](https://java-design-patterns.com/patterns/builder/) and functional interfaces.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the builder pattern using functional interfaces to create a more readable book
|
||||||
|
* creator function. This function is equivalent to the BOOK_CREATOR function.
|
||||||
|
*/
|
||||||
public static AddGenre builder() {
|
public static AddGenre builder() {
|
||||||
return genre
|
return genre
|
||||||
-> author
|
-> author
|
||||||
@@ -90,18 +103,30 @@ public static AddGenre builder(){
|
|||||||
-> new Book(genre, author, title, publicationDate);
|
-> new Book(genre, author, title, publicationDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional interface which adds the genre to a book.
|
||||||
|
*/
|
||||||
public interface AddGenre {
|
public interface AddGenre {
|
||||||
Book.AddAuthor withGenre(Genre genre);
|
Book.AddAuthor withGenre(Genre genre);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional interface which adds the author to a book.
|
||||||
|
*/
|
||||||
public interface AddAuthor {
|
public interface AddAuthor {
|
||||||
Book.AddTitle withAuthor(String author);
|
Book.AddTitle withAuthor(String author);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional interface which adds the title to a book.
|
||||||
|
*/
|
||||||
public interface AddTitle {
|
public interface AddTitle {
|
||||||
Book.AddPublicationDate withTitle(String title);
|
Book.AddPublicationDate withTitle(String title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional interface which adds the publication date to a book.
|
||||||
|
*/
|
||||||
public interface AddPublicationDate {
|
public interface AddPublicationDate {
|
||||||
Book withPublicationDate(LocalDate publicationDate);
|
Book withPublicationDate(LocalDate publicationDate);
|
||||||
}
|
}
|
||||||
@@ -125,7 +150,7 @@ public static void main(String[]args){
|
|||||||
// Defining genre book functions
|
// Defining genre book functions
|
||||||
Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
|
Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
|
||||||
Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
|
Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
|
||||||
Book.AddAuthor scifiBookFunc=Book.builder().withGenre(Genre.SCI_FI);
|
Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCIFI);
|
||||||
|
|
||||||
// Defining author book functions
|
// Defining author book functions
|
||||||
Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
|
Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
|
||||||
@@ -166,20 +191,20 @@ public static void main(String[]args){
|
|||||||
Program output:
|
Program output:
|
||||||
|
|
||||||
```
|
```
|
||||||
Librarian begins their work.
|
09:04:52.499 [main] INFO com.iluwatar.currying.App -- Librarian begins their work.
|
||||||
Stephen King Books:
|
09:04:52.502 [main] INFO com.iluwatar.currying.App -- Stephen King Books:
|
||||||
Book{genre=HORROR, author='Stephen King', title='The Shining', publicationDate=1977-01-28}
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=HORROR, author='Stephen King', title='The Shining', publicationDate=1977-01-28}
|
||||||
Book{genre=FANTASY, author='Stephen King', title='The Dark Tower: Gunslinger', publicationDate=1982-06-10}
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=FANTASY, author='Stephen King', title='The Dark Tower: Gunslinger', publicationDate=1982-06-10}
|
||||||
J.K. Rowling Books:
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- J.K. Rowling Books:
|
||||||
Book{genre=FANTASY, author='J.K. Rowling', title='Harry Potter and the Chamber of Secrets', publicationDate=1998-07-02}
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=FANTASY, author='J.K. Rowling', title='Harry Potter and the Chamber of Secrets', publicationDate=1998-07-02}
|
||||||
Sci-fi Books:
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Sci-fi Books:
|
||||||
Book{genre=SCI_FI, author='Frank Herbert', title='Dune', publicationDate=1965-08-01}
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Frank Herbert', title='Dune', publicationDate=1965-08-01}
|
||||||
Book{genre=SCI_FI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
|
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
@@ -187,6 +212,12 @@ Book{genre=SCI_FI, author='Isaac Asimov', title='Foundation', publicationDate=19
|
|||||||
* In functional programming languages or paradigms to simplify functions that take multiple arguments.
|
* In functional programming languages or paradigms to simplify functions that take multiple arguments.
|
||||||
* To improve code reusability and composability by breaking down functions into simpler, unary functions.
|
* To improve code reusability and composability by breaking down functions into simpler, unary functions.
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
* [Currying in Java (Baeldung)](https://www.baeldung.com/java-currying)
|
||||||
|
* [What Is Currying in Programming (Towards Data Science)](https://towardsdatascience.com/what-is-currying-in-programming-56fd57103431#:~:text=Currying%20is%20helpful%20when%20you,concise%2C%20and%20more%20readable%20solution.)
|
||||||
|
* [Why the fudge should I use currying? (DailyJS)](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)
|
||||||
|
|
||||||
## Known uses
|
## Known uses
|
||||||
|
|
||||||
* Functional programming languages like Haskell, Scala, and JavaScript.
|
* Functional programming languages like Haskell, Scala, and JavaScript.
|
||||||
@@ -216,9 +247,6 @@ Trade-offs:
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
* [Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions](https://amzn.to/3TKeZPD)
|
||||||
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3J6vEaW)
|
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3J6vEaW)
|
||||||
* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://amzn.to/3J6vJLM)
|
* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://amzn.to/3J6vJLM)
|
||||||
* [Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions](https://amzn.to/3TKeZPD)
|
|
||||||
* [Currying in Java](https://www.baeldung.com/java-currying)
|
|
||||||
* [What Is Currying in Programming](https://towardsdatascience.com/what-is-currying-in-programming-56fd57103431#:~:text=Currying%20is%20helpful%20when%20you,concise%2C%20and%20more%20readable%20solution.)
|
|
||||||
* [Why the fudge should I use currying?](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)
|
|
||||||
Reference in new issue
Block a user