diff --git a/currying/README.md b/currying/README.md index 19bc5aa72..c804fca23 100644 --- a/currying/README.md +++ b/currying/README.md @@ -3,7 +3,10 @@ title: Currying category: Functional language: en tag: + - Code simplification - Functional decomposition + - Generic + - Immutable --- ## Also known as @@ -18,7 +21,7 @@ Currying decomposes a function that takes multiple arguments into a sequence of 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 @@ -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. -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. @@ -50,58 +55,78 @@ public class Book { public enum Genre { FANTASY, HORROR, - SCI_FI; + SCI_FI } ``` We could easily create a `Book` object with the following method: ```java -Book createBook(Genre genre,String author,String title,LocalDate publicationDate){ - return new Book(genre,author,title,publicationDate); - } +Book createBook(Genre genre, String author, String title, LocalDate publicationDate) { + return new Book(genre, author, title, 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 -static Function>>>book_creator - =genre - ->author - ->title - ->publicationDate - ->new Book(genre,author,title,publicationDate); +/** + * Curried book builder/creator function. + */ +static Function>>> book_creator + = bookGenre + -> bookAuthor + -> bookTitle + -> bookPublicationDate + -> 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: ```java -Function>>fantasyBookFunc=Book.book_creator.apply(Genre.FANTASY); +Function>> 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 -public static AddGenre builder(){ - return genre - ->author - ->title - ->publicationDate - ->new Book(genre,author,title,publicationDate); - } +/** + * 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() { + return genre + -> author + -> title + -> publicationDate + -> new Book(genre, author, title, publicationDate); +} + +/** + * Functional interface which adds the genre to a book. + */ public interface AddGenre { Book.AddAuthor withGenre(Genre genre); } +/** + * Functional interface which adds the author to a book. + */ public interface AddAuthor { Book.AddTitle withAuthor(String author); } +/** + * Functional interface which adds the title to a book. + */ public interface AddTitle { Book.AddPublicationDate withTitle(String title); } +/** + * Functional interface which adds the publication date to a book. + */ public interface AddPublicationDate { Book withPublicationDate(LocalDate publicationDate); } @@ -110,76 +135,76 @@ public interface AddPublicationDate { 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: ```java -Book book=Book.builder().withGenre(Genre.FANTASY) - .withAuthor("Author") - .withTitle("Title") - .withPublicationDate(LocalDate.of(2000,7,2)); +Book book = Book.builder().withGenre(Genre.FANTASY) + .withAuthor("Author") + .withTitle("Title") + .withPublicationDate(LocalDate.of(2000, 7, 2)); ``` The below example demonstrates how partial application can be used with the `builder` function to create specialised book builder functions. ```java -public static void main(String[]args){ - LOGGER.info("Librarian begins their work."); +public static void main(String[] args) { + LOGGER.info("Librarian begins their work."); - // Defining genre book functions - Book.AddAuthor fantasyBookFunc=Book.builder().withGenre(Genre.FANTASY); - Book.AddAuthor horrorBookFunc=Book.builder().withGenre(Genre.HORROR); - Book.AddAuthor scifiBookFunc=Book.builder().withGenre(Genre.SCI_FI); + // Defining genre book functions + Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY); + Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR); + Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCIFI); - // Defining author book functions - Book.AddTitle kingFantasyBooksFunc=fantasyBookFunc.withAuthor("Stephen King"); - Book.AddTitle kingHorrorBooksFunc=horrorBookFunc.withAuthor("Stephen King"); - Book.AddTitle rowlingFantasyBooksFunc=fantasyBookFunc.withAuthor("J.K. Rowling"); + // Defining author book functions + Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King"); + Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King"); + Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling"); - // Creates books by Stephen King (horror and fantasy genres) - Book shining=kingHorrorBooksFunc.withTitle("The Shining") - .withPublicationDate(LocalDate.of(1977,1,28)); - Book darkTower=kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger") - .withPublicationDate(LocalDate.of(1982,6,10)); + // Creates books by Stephen King (horror and fantasy genres) + Book shining = kingHorrorBooksFunc.withTitle("The Shining") + .withPublicationDate(LocalDate.of(1977, 1, 28)); + Book darkTower = kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger") + .withPublicationDate(LocalDate.of(1982, 6, 10)); - // Creates fantasy books by J.K. Rowling - Book chamberOfSecrets=rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets") - .withPublicationDate(LocalDate.of(1998,7,2)); + // Creates fantasy books by J.K. Rowling + Book chamberOfSecrets = rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets") + .withPublicationDate(LocalDate.of(1998, 7, 2)); - // Create sci-fi books - Book dune=scifiBookFunc.withAuthor("Frank Herbert") - .withTitle("Dune") - .withPublicationDate(LocalDate.of(1965,8,1)); - Book foundation=scifiBookFunc.withAuthor("Isaac Asimov") - .withTitle("Foundation") - .withPublicationDate(LocalDate.of(1942,5,1)); + // Create sci-fi books + Book dune = scifiBookFunc.withAuthor("Frank Herbert") + .withTitle("Dune") + .withPublicationDate(LocalDate.of(1965, 8, 1)); + Book foundation = scifiBookFunc.withAuthor("Isaac Asimov") + .withTitle("Foundation") + .withPublicationDate(LocalDate.of(1942, 5, 1)); - LOGGER.info("Stephen King Books:"); - LOGGER.info(shining.toString()); - LOGGER.info(darkTower.toString()); + LOGGER.info("Stephen King Books:"); + LOGGER.info(shining.toString()); + LOGGER.info(darkTower.toString()); - LOGGER.info("J.K. Rowling Books:"); - LOGGER.info(chamberOfSecrets.toString()); + LOGGER.info("J.K. Rowling Books:"); + LOGGER.info(chamberOfSecrets.toString()); - LOGGER.info("Sci-fi Books:"); - LOGGER.info(dune.toString()); - LOGGER.info(foundation.toString()); - } + LOGGER.info("Sci-fi Books:"); + LOGGER.info(dune.toString()); + LOGGER.info(foundation.toString()); +} ``` Program output: ``` -Librarian begins their work. -Stephen King Books: -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} -J.K. Rowling Books: -Book{genre=FANTASY, author='J.K. Rowling', title='Harry Potter and the Chamber of Secrets', publicationDate=1998-07-02} -Sci-fi Books: -Book{genre=SCI_FI, 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.499 [main] INFO com.iluwatar.currying.App -- Librarian begins their work. +09:04:52.502 [main] INFO com.iluwatar.currying.App -- Stephen King Books: +09:04:52.506 [main] INFO com.iluwatar.currying.App -- 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=FANTASY, author='Stephen King', title='The Dark Tower: Gunslinger', publicationDate=1982-06-10} +09:04:52.506 [main] INFO com.iluwatar.currying.App -- J.K. Rowling Books: +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} +09:04:52.506 [main] INFO com.iluwatar.currying.App -- Sci-fi Books: +09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Frank Herbert', title='Dune', publicationDate=1965-08-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 -![currying-uml](./etc/currying.urm.png) +![Currying](./etc/currying.urm.png) ## 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. * 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 * Functional programming languages like Haskell, Scala, and JavaScript. @@ -216,9 +247,6 @@ Trade-offs: ## 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) * [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)