diff --git a/currying/README.md b/currying/README.md new file mode 100644 index 000000000..72e0e89d4 --- /dev/null +++ b/currying/README.md @@ -0,0 +1,191 @@ +--- +title: Currying +category: Functional +language: en +tag: +- Decoupling +--- + +## Name / classification +Currying + +## Intent +Currying decomposes a function that takes multiple arguments into a sequence of functions that each take a single argument. +Curried functions are useful since they can be used to create new functions with lower arity to perform more specialised tasks +in a concise and readable manner. This is done via partial application. + +## Explanation +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. + +In plain words +> Decompose a function that take multiple arguments into multiple functions that take a single argument. + +Wikipedia says +> Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that +> each take a single argument. Given a function $f:(X \times Y) \rightarrow Z$, currying constructs a new function +> $h:X \rightarrow (Y\rightarrow Z)$. $h$ takes an argument from $X$ and returns a function which maps $Y$ to $Z$. Hence, +> $h(x)(y) = f(x, y)$. + +Programmatic example +We have a `Book` class and `Genre` enum. +```java +public class Book { + private final Genre genre; + private final String author; + private final String title; + private final LocalDate publicationDate; + + Book(Genre genre, String author, String title, LocalDate publicationDate) { + this.genre = genre; + this.author = author; + this.title = title; + this.publicationDate = publicationDate; + } +} + +public enum Genre { + FANTASY, + HORROR, + 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); +} +``` +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. +```java +static Function>>> book_creator + = genre + -> author + -> title + -> publicationDate + -> new Book(genre, author, title, publicationDate); +``` +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); +``` +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). +```java +public static AddGenre builder() { + return genre + -> author + -> title + -> publicationDate + -> new Book(genre, author, title, publicationDate); +} + +public interface AddGenre { +Book.AddAuthor withGenre(Genre genre); +} + +public interface AddAuthor { +Book.AddTitle withAuthor(String author); +} + +public interface AddTitle { +Book.AddPublicationDate withTitle(String title); +} + +public interface AddPublicationDate { +Book withPublicationDate(LocalDate publicationDate); +} +``` +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.FANTAST) + .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."); + + // 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 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 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)); + + 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("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} +``` + +## Class diagram +![currying-uml](./etc/currying.urm.png) + +## Applicability +A curried function which has only been passed some of its arguments is called a partial application. Partial application +allows for the creation of functions with some pre-defined data in their scope, since partial application can be used to +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. + +## Known uses +Most functional programming languages support curried functions. A popular example is [Haskell](https://www.haskell.org/), in which all functions are considered curried. + +## Consequences +Pros +* Currying allows for partial application, which can be used to create specialised functions concisely. + +Cons +* 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. +* As shown in the programmatic example above, curried functions with several parameters have a cumbersome type signature (in Java). + +## Related patterns +* [Builder patter](https://java-design-patterns.com/patterns/builder/) + +## Credits +* [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) \ No newline at end of file diff --git a/currying/etc/currying.urm.png b/currying/etc/currying.urm.png new file mode 100644 index 000000000..c672bdde3 Binary files /dev/null and b/currying/etc/currying.urm.png differ diff --git a/currying/etc/currying.urm.puml b/currying/etc/currying.urm.puml new file mode 100644 index 000000000..9b336ff90 --- /dev/null +++ b/currying/etc/currying.urm.puml @@ -0,0 +1,39 @@ +@startuml +package com.iluwatar.currying { + class App { + + App() + + main(args : String[]) {static} + } + class Book { + ~ BOOK_CREATOR : Function>>> {static} + - author : String + - genre : Genre + - publicationDate : LocalDate + - title : String + ~ Book(genre : Genre, author : String, title : String, publicationDate : LocalDate) + + builder() : AddGenre {static} + } + interface AddAuthor { + + withAuthor(String) : AddTitle {abstract} + } + interface AddGenre { + + withGenre(Genre) : AddAuthor {abstract} + } + interface AddPublicationDate { + + withPublicationDate(LocalDate) : Book {abstract} + } + interface AddTitle { + + withTitle(String) : AddPublicationDate {abstract} + } + enum Genre { + + FANTASY {static} + + HORROR {static} + + SCI_FI {static} + } +} +Book --> "-genre" Genre +AddPublicationDate ..+ Book +AddAuthor ..+ Book +AddTitle ..+ Book +AddGenre ..+ Book +@enduml \ No newline at end of file diff --git a/currying/pom.xml b/currying/pom.xml new file mode 100644 index 000000000..5b5385c45 --- /dev/null +++ b/currying/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + currying + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.currying.App + + + + + + + + + \ No newline at end of file diff --git a/currying/src/main/java/com/iluwatar/currying/App.java b/currying/src/main/java/com/iluwatar/currying/App.java new file mode 100644 index 000000000..d3ca262b5 --- /dev/null +++ b/currying/src/main/java/com/iluwatar/currying/App.java @@ -0,0 +1,86 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.currying; + +import java.time.LocalDate; +import lombok.extern.slf4j.Slf4j; + +/** +* Currying decomposes a function with multiple arguments in multiple functions that +* take a single argument. A curried function which has only been passed some of its +* arguments is called a partial application. Partial application is useful since it can +* be used to create specialised functions in a concise way. +* +*

In this example, a librarian uses a curried book builder function create books belonging to +* desired genres and written by specific authors. +*/ +@Slf4j +public class App { + /** + * Main entry point of the program. + */ + 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.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"); + + // 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)); + + // 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("J.K. Rowling Books:"); + LOGGER.info(chamberOfSecrets.toString()); + + LOGGER.info("Sci-fi Books:"); + LOGGER.info(dune.toString()); + LOGGER.info(foundation.toString()); + } +} \ No newline at end of file diff --git a/currying/src/main/java/com/iluwatar/currying/Book.java b/currying/src/main/java/com/iluwatar/currying/Book.java new file mode 100644 index 000000000..f9b7be051 --- /dev/null +++ b/currying/src/main/java/com/iluwatar/currying/Book.java @@ -0,0 +1,117 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.currying; + +import java.time.LocalDate; +import java.util.Objects; +import java.util.function.Function; +import lombok.AllArgsConstructor; + +/** + * Book class. + */ +@AllArgsConstructor +public class Book { + private final Genre genre; + private final String author; + private final String title; + private final LocalDate publicationDate; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Book book = (Book) o; + return Objects.equals(author, book.author) + && Objects.equals(genre, book.genre) + && Objects.equals(title, book.title) + && Objects.equals(publicationDate, book.publicationDate); + } + + @Override + public int hashCode() { + return Objects.hash(author, genre, title, publicationDate); + } + + @Override + public String toString() { + return "Book{" + "genre=" + genre + ", author='" + author + '\'' + + ", title='" + title + '\'' + ", publicationDate=" + publicationDate + '}'; + } + + /** + * Curried book builder/creator function. + */ + static Function>>> book_creator + = bookGenre + -> bookAuthor + -> bookTitle + -> bookPublicationDate + -> new Book(bookGenre, bookAuthor, bookTitle, bookPublicationDate); + + /** + * 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); + } +} \ No newline at end of file diff --git a/currying/src/main/java/com/iluwatar/currying/Genre.java b/currying/src/main/java/com/iluwatar/currying/Genre.java new file mode 100644 index 000000000..58e639f4c --- /dev/null +++ b/currying/src/main/java/com/iluwatar/currying/Genre.java @@ -0,0 +1,34 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.currying; + +/** + * Enum representing different book genres. + */ +public enum Genre { + FANTASY, + HORROR, + SCIFI; +} diff --git a/currying/src/test/java/com/iluwatar/currying/AppTest.java b/currying/src/test/java/com/iluwatar/currying/AppTest.java new file mode 100644 index 000000000..83fd99363 --- /dev/null +++ b/currying/src/test/java/com/iluwatar/currying/AppTest.java @@ -0,0 +1,39 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.currying; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +/** + * Tests that the App can be run without throwing any exceptions. + */ +class AppTest { + @Test + void executesWithoutExceptions() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/currying/src/test/java/com/iluwatar/currying/BookCurryingTest.java b/currying/src/test/java/com/iluwatar/currying/BookCurryingTest.java new file mode 100644 index 000000000..437941583 --- /dev/null +++ b/currying/src/test/java/com/iluwatar/currying/BookCurryingTest.java @@ -0,0 +1,83 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.currying; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +/** + * Unit tests for the Book class + */ +class BookCurryingTest { + private static Book expectedBook; + + @BeforeAll + public static void initialiseBook() { + expectedBook = new Book(Genre.FANTASY, + "Dave", + "Into the Night", + LocalDate.of(2002, 4, 7)); + } + + /** + * Tests that the expected book can be created via curried functions + */ + @Test + void createsExpectedBook() { + Book builderCurriedBook = Book.builder() + .withGenre(Genre.FANTASY) + .withAuthor("Dave") + .withTitle("Into the Night") + .withPublicationDate(LocalDate.of(2002, 4, 7)); + + Book funcCurriedBook = Book.book_creator + .apply(Genre.FANTASY) + .apply("Dave") + .apply("Into the Night") + .apply(LocalDate.of(2002, 4, 7)); + + assertEquals(expectedBook, builderCurriedBook); + assertEquals(expectedBook, funcCurriedBook); + } + + /** + * Tests that an intermediate curried function can be used to create the expected book + */ + @Test + void functionCreatesExpectedBook() { + Book.AddTitle daveFantasyBookFunc = Book.builder() + .withGenre(Genre.FANTASY) + .withAuthor("Dave"); + + Book curriedBook = daveFantasyBookFunc.withTitle("Into the Night") + .withPublicationDate(LocalDate.of(2002, 4, 7)); + + assertEquals(expectedBook, curriedBook); + } +} diff --git a/pom.xml b/pom.xml index ee3465ca3..68714e28a 100644 --- a/pom.xml +++ b/pom.xml @@ -198,6 +198,7 @@ composite-view metadata-mapping service-to-worker + currying serialized-entity identity-map component