Skip to content

Commit b1f8a16

Browse files
committed
docs: update currying
1 parent 85823b4 commit b1f8a16

File tree

2 files changed

+142
-109
lines changed

2 files changed

+142
-109
lines changed

currying/README.md

+141-108
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,170 @@
11
---
22
title: Currying
3-
category: Functional
3+
category: Functional
44
language: en
55
tag:
6-
- Decoupling
6+
- Functional decomposition
77
---
88

9-
## Name / classification
10-
Currying
9+
## Also known as
10+
11+
* Partial Function Application
1112

1213
## Intent
13-
Currying decomposes a function that takes multiple arguments into a sequence of functions that each take a single argument.
14-
Curried functions are useful since they can be used to create new functions with lower arity to perform more specialised tasks
15-
in a concise and readable manner. This is done via partial application.
14+
15+
Currying decomposes a function that takes multiple arguments into a sequence of functions that each take a single argument. It helps in creating a higher-order function by partial application of its arguments.
1616

1717
## Explanation
18+
1819
Real-world example
19-
> Consider a librarian who wants to populate their library with books. The librarian wants functions which can create
20-
> books corresponding to specific genres and authors. Currying makes this possible by writing a curried book builder
21-
> function and utilising partial application.
20+
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.
2222
2323
In plain words
24-
> Decompose a function that take multiple arguments into multiple functions that take a single argument.
24+
25+
> Decompose a function that take multiple arguments into multiple functions that take a single argument.
2526
2627
Wikipedia says
27-
> Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that
28-
> each take a single argument. Given a function $f:(X \times Y) \rightarrow Z$, currying constructs a new function
29-
> $h:X \rightarrow (Y\rightarrow Z)$. $h$ takes an argument from $X$ and returns a function which maps $Y$ to $Z$. Hence,
30-
> $h(x)(y) = f(x, y)$.
3128

32-
Programmatic example
29+
> 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.
30+
31+
Programmatic example
32+
3333
We have a `Book` class and `Genre` enum.
34+
3435
```java
3536
public class Book {
36-
private final Genre genre;
37-
private final String author;
38-
private final String title;
39-
private final LocalDate publicationDate;
40-
41-
Book(Genre genre, String author, String title, LocalDate publicationDate) {
42-
this.genre = genre;
43-
this.author = author;
44-
this.title = title;
45-
this.publicationDate = publicationDate;
46-
}
37+
private final Genre genre;
38+
private final String author;
39+
private final String title;
40+
private final LocalDate publicationDate;
41+
42+
Book(Genre genre, String author, String title, LocalDate publicationDate) {
43+
this.genre = genre;
44+
this.author = author;
45+
this.title = title;
46+
this.publicationDate = publicationDate;
47+
}
4748
}
4849

4950
public enum Genre {
50-
FANTASY,
51-
HORROR,
52-
SCI_FI;
51+
FANTASY,
52+
HORROR,
53+
SCI_FI;
5354
}
5455
```
56+
5557
We could easily create a `Book` object with the following method:
58+
5659
```java
57-
Book createBook(Genre genre, String author, String title, LocalDate publicationDate) {
58-
return new Book(genre, author, title, publicationDate);
59-
}
60+
Book createBook(Genre genre,String author,String title,LocalDate publicationDate){
61+
return new Book(genre,author,title,publicationDate);
62+
}
6063
```
64+
6165
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.
66+
6267
```java
63-
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
64-
= genre
65-
-> author
66-
-> title
67-
-> publicationDate
68-
-> new Book(genre, author, title, publicationDate);
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);
6974
```
75+
7076
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:
77+
7178
```java
72-
Function<String, Function<String, Function<LocalDate, Book>>> fantasyBookFunc = Book.book_creator.apply(Genre.FANTASY);
79+
Function<String, Function<String, Function<LocalDate, Book>>>fantasyBookFunc=Book.book_creator.apply(Genre.FANTASY);
7380
```
74-
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).
81+
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).
83+
7584
```java
76-
public static AddGenre builder() {
77-
return genre
78-
-> author
79-
-> title
80-
-> publicationDate
81-
-> new Book(genre, author, title, publicationDate);
82-
}
85+
public static AddGenre builder(){
86+
return genre
87+
->author
88+
->title
89+
->publicationDate
90+
->new Book(genre,author,title,publicationDate);
91+
}
8392

8493
public interface AddGenre {
85-
Book.AddAuthor withGenre(Genre genre);
94+
Book.AddAuthor withGenre(Genre genre);
8695
}
8796

8897
public interface AddAuthor {
89-
Book.AddTitle withAuthor(String author);
98+
Book.AddTitle withAuthor(String author);
9099
}
91100

92101
public interface AddTitle {
93-
Book.AddPublicationDate withTitle(String title);
102+
Book.AddPublicationDate withTitle(String title);
94103
}
95104

96105
public interface AddPublicationDate {
97-
Book withPublicationDate(LocalDate publicationDate);
106+
Book withPublicationDate(LocalDate publicationDate);
98107
}
99108
```
100-
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`.
101-
For example, we could create a `Book` as follows:
109+
110+
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:
111+
102112
```java
103-
Book book = Book.builder().withGenre(Genre.FANTASY)
113+
Book book=Book.builder().withGenre(Genre.FANTASY)
104114
.withAuthor("Author")
105115
.withTitle("Title")
106-
.withPublicationDate(LocalDate.of(2000, 7, 2));
116+
.withPublicationDate(LocalDate.of(2000,7,2));
107117
```
118+
108119
The below example demonstrates how partial application can be used with the `builder` function to create specialised book builder functions.
120+
109121
```java
110-
public static void main(String[] args) {
111-
LOGGER.info("Librarian begins their work.");
112-
113-
// Defining genre book functions
114-
Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
115-
Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
116-
Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCI_FI);
117-
118-
// Defining author book functions
119-
Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
120-
Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King");
121-
Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling");
122-
123-
// Creates books by Stephen King (horror and fantasy genres)
124-
Book shining = kingHorrorBooksFunc.withTitle("The Shining")
125-
.withPublicationDate(LocalDate.of(1977, 1, 28));
126-
Book darkTower = kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger")
127-
.withPublicationDate(LocalDate.of(1982, 6, 10));
128-
129-
// Creates fantasy books by J.K. Rowling
130-
Book chamberOfSecrets = rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets")
131-
.withPublicationDate(LocalDate.of(1998, 7, 2));
132-
133-
// Create sci-fi books
134-
Book dune = scifiBookFunc.withAuthor("Frank Herbert")
135-
.withTitle("Dune")
136-
.withPublicationDate(LocalDate.of(1965, 8, 1));
137-
Book foundation = scifiBookFunc.withAuthor("Isaac Asimov")
138-
.withTitle("Foundation")
139-
.withPublicationDate(LocalDate.of(1942, 5, 1));
140-
141-
LOGGER.info("Stephen King Books:");
142-
LOGGER.info(shining.toString());
143-
LOGGER.info(darkTower.toString());
144-
145-
LOGGER.info("J.K. Rowling Books:");
146-
LOGGER.info(chamberOfSecrets.toString());
147-
148-
LOGGER.info("Sci-fi Books:");
149-
LOGGER.info(dune.toString());
150-
LOGGER.info(foundation.toString());
151-
}
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+
}
152164
```
165+
153166
Program output:
167+
154168
```
155169
Librarian begins their work.
156170
Stephen King Books:
@@ -164,28 +178,47 @@ Book{genre=SCI_FI, author='Isaac Asimov', title='Foundation', publicationDate=19
164178
```
165179

166180
## Class diagram
181+
167182
![currying-uml](./etc/currying.urm.png)
168183

169184
## Applicability
170-
A curried function which has only been passed some of its arguments is called a partial application. Partial application
171-
allows for the creation of functions with some pre-defined data in their scope, since partial application can be used to
172-
create specialised functions with lower arity. This abstraction can help keep code readable and concise. Therefore, currying is useful when frequently calling functions with fixed parameters.
185+
186+
* When functions need to be called with some arguments preset.
187+
* In functional programming languages or paradigms to simplify functions that take multiple arguments.
188+
* To improve code reusability and composability by breaking down functions into simpler, unary functions.
173189

174190
## Known uses
175-
Most functional programming languages support curried functions. A popular example is [Haskell](https://www.haskell.org/), in which all functions are considered curried.
191+
192+
* Functional programming languages like Haskell, Scala, and JavaScript.
193+
* Event handling in UIs where a function with specific parameters needs to be triggered upon an event.
194+
* APIs that require configuration with multiple parameters.
176195

177196
## Consequences
178-
Pros
179-
* Currying allows for partial application, which can be used to create specialised functions concisely.
180197

181-
Cons
182-
* The order of the parameters in a curried function is important since we want to take advantage of partial application. It is best to input the most general parameters first and input specific parameters last.
183-
* As shown in the programmatic example above, curried functions with several parameters have a cumbersome type signature (in Java).
198+
Benefits:
199+
200+
* Increases function reusability by allowing the creation of specialized functions from more generic ones.
201+
* Enhances code readability and maintainability by breaking complex functions into simpler, single-argument functions.
202+
* Facilitates function composition, leading to more declarative and concise code.
203+
204+
Trade-offs:
205+
206+
* Can lead to performance overhead due to the creation of additional closures.
207+
* May make debugging more challenging, as it introduces additional layers of function calls.
208+
* Can be less intuitive for developers unfamiliar with functional programming concepts.
209+
* As shown in the programmatic example above, curried functions with several parameters have a cumbersome type signature in Java.
184210

185211
## Related patterns
186-
* [Builder pattern](https://java-design-patterns.com/patterns/builder/)
212+
213+
* Function Composition: Currying is often used in conjunction with function composition to enable more readable and concise code.
214+
* [Decorator](https://java-design-patterns.com/patterns/decorator/): While not the same, currying shares the decorator pattern's concept of wrapping functionality.
215+
* [Factory](https://java-design-patterns.com/patterns/factory/): Currying can be used to create factory functions that produce variations of a function with certain arguments preset.
187216

188217
## Credits
218+
219+
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3J6vEaW)
220+
* [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)
189222
* [Currying in Java](https://www.baeldung.com/java-currying)
190223
* [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.)
191224
* [Why the fudge should I use currying?](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)

currying/src/main/java/com/iluwatar/currying/Genre.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@
3030
public enum Genre {
3131
FANTASY,
3232
HORROR,
33-
SCIFI;
33+
SCIFI
3434
}

0 commit comments

Comments
 (0)