Skip to content

Commit 6cbcf0f

Browse files
committed
docs: update currying
1 parent 0c31312 commit 6cbcf0f

File tree

1 file changed

+110
-82
lines changed

1 file changed

+110
-82
lines changed

currying/README.md

Lines changed: 110 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ title: Currying
33
category: Functional
44
language: en
55
tag:
6+
- Code simplification
67
- Functional decomposition
8+
- Generic
9+
- Immutable
710
---
811

912
## Also known as
@@ -18,7 +21,7 @@ Currying decomposes a function that takes multiple arguments into a sequence of
1821

1922
Real-world example
2023

21-
> 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.
24+
> 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.
2225
2326
In plain words
2427

@@ -28,7 +31,9 @@ Wikipedia says
2831

2932
> 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.
3033
31-
Programmatic example
34+
**Programmatic example**
35+
36+
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.
3237

3338
We have a `Book` class and `Genre` enum.
3439

@@ -50,58 +55,78 @@ public class Book {
5055
public enum Genre {
5156
FANTASY,
5257
HORROR,
53-
SCI_FI;
58+
SCI_FI
5459
}
5560
```
5661

5762
We could easily create a `Book` object with the following method:
5863

5964
```java
60-
Book createBook(Genre genre,String author,String title,LocalDate publicationDate){
61-
return new Book(genre,author,title,publicationDate);
62-
}
65+
Book createBook(Genre genre, String author, String title, LocalDate publicationDate) {
66+
return new Book(genre, author, title, publicationDate);
67+
}
6368
```
6469

65-
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.
70+
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.
6671

6772
```java
68-
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>>book_creator
69-
=genre
70-
->author
71-
->title
72-
->publicationDate
73-
->new Book(genre,author,title,publicationDate);
73+
/**
74+
* Curried book builder/creator function.
75+
*/
76+
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
77+
= bookGenre
78+
-> bookAuthor
79+
-> bookTitle
80+
-> bookPublicationDate
81+
-> new Book(bookGenre, bookAuthor, bookTitle, bookPublicationDate);
7482
```
7583

7684
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:
7785

7886
```java
79-
Function<String, Function<String, Function<LocalDate, Book>>>fantasyBookFunc=Book.book_creator.apply(Genre.FANTASY);
87+
Function<String, Function<String, Function<LocalDate, Book>>> fantasyBookFunc = Book.book_creator.apply(Genre.FANTASY);
8088
```
8189

82-
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).
90+
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.
8391

8492
```java
85-
public static AddGenre builder(){
86-
return genre
87-
->author
88-
->title
89-
->publicationDate
90-
->new Book(genre,author,title,publicationDate);
91-
}
9293

94+
/**
95+
* Implements the builder pattern using functional interfaces to create a more readable book
96+
* creator function. This function is equivalent to the BOOK_CREATOR function.
97+
*/
98+
public static AddGenre builder() {
99+
return genre
100+
-> author
101+
-> title
102+
-> publicationDate
103+
-> new Book(genre, author, title, publicationDate);
104+
}
105+
106+
/**
107+
* Functional interface which adds the genre to a book.
108+
*/
93109
public interface AddGenre {
94110
Book.AddAuthor withGenre(Genre genre);
95111
}
96112

113+
/**
114+
* Functional interface which adds the author to a book.
115+
*/
97116
public interface AddAuthor {
98117
Book.AddTitle withAuthor(String author);
99118
}
100119

120+
/**
121+
* Functional interface which adds the title to a book.
122+
*/
101123
public interface AddTitle {
102124
Book.AddPublicationDate withTitle(String title);
103125
}
104126

127+
/**
128+
* Functional interface which adds the publication date to a book.
129+
*/
105130
public interface AddPublicationDate {
106131
Book withPublicationDate(LocalDate publicationDate);
107132
}
@@ -110,83 +135,89 @@ public interface AddPublicationDate {
110135
The semantics of the `builder` function can easily be understood. The `builder` function returns a function `AddGenre`, which adds the genre to the book. Similarity, the `AddGenre` function returns another function `AddTitle`, which adds the title to the book and so on, until the `AddPublicationDate` function returns a `Book`. For example, we could create a `Book` as follows:
111136

112137
```java
113-
Book book=Book.builder().withGenre(Genre.FANTASY)
114-
.withAuthor("Author")
115-
.withTitle("Title")
116-
.withPublicationDate(LocalDate.of(2000,7,2));
138+
Book book = Book.builder().withGenre(Genre.FANTASY)
139+
.withAuthor("Author")
140+
.withTitle("Title")
141+
.withPublicationDate(LocalDate.of(2000, 7, 2));
117142
```
118143

119144
The below example demonstrates how partial application can be used with the `builder` function to create specialised book builder functions.
120145

121146
```java
122-
public static void main(String[]args){
123-
LOGGER.info("Librarian begins their work.");
124-
125-
// Defining genre book functions
126-
Book.AddAuthor fantasyBookFunc=Book.builder().withGenre(Genre.FANTASY);
127-
Book.AddAuthor horrorBookFunc=Book.builder().withGenre(Genre.HORROR);
128-
Book.AddAuthor scifiBookFunc=Book.builder().withGenre(Genre.SCI_FI);
129-
130-
// Defining author book functions
131-
Book.AddTitle kingFantasyBooksFunc=fantasyBookFunc.withAuthor("Stephen King");
132-
Book.AddTitle kingHorrorBooksFunc=horrorBookFunc.withAuthor("Stephen King");
133-
Book.AddTitle rowlingFantasyBooksFunc=fantasyBookFunc.withAuthor("J.K. Rowling");
134-
135-
// Creates books by Stephen King (horror and fantasy genres)
136-
Book shining=kingHorrorBooksFunc.withTitle("The Shining")
137-
.withPublicationDate(LocalDate.of(1977,1,28));
138-
Book darkTower=kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger")
139-
.withPublicationDate(LocalDate.of(1982,6,10));
140-
141-
// Creates fantasy books by J.K. Rowling
142-
Book chamberOfSecrets=rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets")
143-
.withPublicationDate(LocalDate.of(1998,7,2));
144-
145-
// Create sci-fi books
146-
Book dune=scifiBookFunc.withAuthor("Frank Herbert")
147-
.withTitle("Dune")
148-
.withPublicationDate(LocalDate.of(1965,8,1));
149-
Book foundation=scifiBookFunc.withAuthor("Isaac Asimov")
150-
.withTitle("Foundation")
151-
.withPublicationDate(LocalDate.of(1942,5,1));
152-
153-
LOGGER.info("Stephen King Books:");
154-
LOGGER.info(shining.toString());
155-
LOGGER.info(darkTower.toString());
156-
157-
LOGGER.info("J.K. Rowling Books:");
158-
LOGGER.info(chamberOfSecrets.toString());
159-
160-
LOGGER.info("Sci-fi Books:");
161-
LOGGER.info(dune.toString());
162-
LOGGER.info(foundation.toString());
163-
}
147+
public static void main(String[] args) {
148+
LOGGER.info("Librarian begins their work.");
149+
150+
// Defining genre book functions
151+
Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
152+
Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
153+
Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCIFI);
154+
155+
// Defining author book functions
156+
Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
157+
Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King");
158+
Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling");
159+
160+
// Creates books by Stephen King (horror and fantasy genres)
161+
Book shining = kingHorrorBooksFunc.withTitle("The Shining")
162+
.withPublicationDate(LocalDate.of(1977, 1, 28));
163+
Book darkTower = kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger")
164+
.withPublicationDate(LocalDate.of(1982, 6, 10));
165+
166+
// Creates fantasy books by J.K. Rowling
167+
Book chamberOfSecrets = rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets")
168+
.withPublicationDate(LocalDate.of(1998, 7, 2));
169+
170+
// Create sci-fi books
171+
Book dune = scifiBookFunc.withAuthor("Frank Herbert")
172+
.withTitle("Dune")
173+
.withPublicationDate(LocalDate.of(1965, 8, 1));
174+
Book foundation = scifiBookFunc.withAuthor("Isaac Asimov")
175+
.withTitle("Foundation")
176+
.withPublicationDate(LocalDate.of(1942, 5, 1));
177+
178+
LOGGER.info("Stephen King Books:");
179+
LOGGER.info(shining.toString());
180+
LOGGER.info(darkTower.toString());
181+
182+
LOGGER.info("J.K. Rowling Books:");
183+
LOGGER.info(chamberOfSecrets.toString());
184+
185+
LOGGER.info("Sci-fi Books:");
186+
LOGGER.info(dune.toString());
187+
LOGGER.info(foundation.toString());
188+
}
164189
```
165190

166191
Program output:
167192

168193
```
169-
Librarian begins their work.
170-
Stephen King Books:
171-
Book{genre=HORROR, author='Stephen King', title='The Shining', publicationDate=1977-01-28}
172-
Book{genre=FANTASY, author='Stephen King', title='The Dark Tower: Gunslinger', publicationDate=1982-06-10}
173-
J.K. Rowling Books:
174-
Book{genre=FANTASY, author='J.K. Rowling', title='Harry Potter and the Chamber of Secrets', publicationDate=1998-07-02}
175-
Sci-fi Books:
176-
Book{genre=SCI_FI, author='Frank Herbert', title='Dune', publicationDate=1965-08-01}
177-
Book{genre=SCI_FI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
194+
09:04:52.499 [main] INFO com.iluwatar.currying.App -- Librarian begins their work.
195+
09:04:52.502 [main] INFO com.iluwatar.currying.App -- Stephen King Books:
196+
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=HORROR, author='Stephen King', title='The Shining', publicationDate=1977-01-28}
197+
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}
198+
09:04:52.506 [main] INFO com.iluwatar.currying.App -- J.K. Rowling Books:
199+
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}
200+
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Sci-fi Books:
201+
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Frank Herbert', title='Dune', publicationDate=1965-08-01}
202+
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
178203
```
179204

180205
## Class diagram
181206

182-
![currying-uml](./etc/currying.urm.png)
207+
![Currying](./etc/currying.urm.png)
183208

184209
## Applicability
185210

186211
* When functions need to be called with some arguments preset.
187212
* In functional programming languages or paradigms to simplify functions that take multiple arguments.
188213
* To improve code reusability and composability by breaking down functions into simpler, unary functions.
189214

215+
## Tutorials
216+
217+
* [Currying in Java (Baeldung)](https://www.baeldung.com/java-currying)
218+
* [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.)
219+
* [Why the fudge should I use currying? (DailyJS)](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)
220+
190221
## Known uses
191222

192223
* Functional programming languages like Haskell, Scala, and JavaScript.
@@ -216,9 +247,6 @@ Trade-offs:
216247

217248
## Credits
218249

250+
* [Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions](https://amzn.to/3TKeZPD)
219251
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3J6vEaW)
220252
* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://amzn.to/3J6vJLM)
221-
* [Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions](https://amzn.to/3TKeZPD)
222-
* [Currying in Java](https://www.baeldung.com/java-currying)
223-
* [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.)
224-
* [Why the fudge should I use currying?](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)

0 commit comments

Comments
 (0)