Repository: iluwatar/java-design-patterns
Issue: Explanation for Combinator pattern
Labels: info: help wanted
type: feature
epic: documentation

In this issue, let's add a proper explanation for the pattern.

Acceptance criteria

- The pattern's README.md has been amended with an explanation
- The explanation consists of
    - Real world example
    - In plain words
    - Wikipedia says
    - Programmatic example

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/combinator/README.md b/combinator/README.md
index 15ec1ea59019..70b6a82f7942 100644
--- a/combinator/README.md
+++ b/combinator/README.md
@@ -1,132 +1,136 @@
 ---
 title: Combinator
-category: Idiom
+category: Functional
 language: en
 tag:
- - Reactive
+    - Idiom
+    - Reactive
 ---
 
 ## Also known as
 
-Composition pattern
+* Function Composition
+* Functional Combinator
 
 ## Intent
 
-The functional pattern representing a style of organizing libraries centered around the idea of combining functions.
-Putting it simply, there is some type T, some functions for constructing “primitive” values of type T, and some “combinators” which can combine values of type T in various ways to build up more complex values of type T.
+The Combinator pattern is intended to enable complex functionalities by combining simple functions into more complex
+ones. It aims to achieve modularization and reusability by breaking down a task into simpler, interchangeable components
+that can be composed in various ways.
 
 ## Explanation
 
 Real world example
 
-> In computer science, combinatory logic is used as a simplified model of computation, used in computability theory and proof theory. Despite its simplicity, combinatory logic captures many essential features of computation.
-> 
+> In computer science, combinatory logic is used as a simplified model of computation, used in computability theory and
+> proof theory. Despite its simplicity, combinatory logic captures many essential features of computation.
 
 In plain words
 
-> The combinator allows you to create new "things" from previously defined "things".
-> 
+> The combinator allows you to create new "things" from previously defined "things"
 
 Wikipedia says
 
-> A combinator is a higher-order function that uses only function application and earlier defined combinators to define a result from its arguments.
-> 
+> A combinator is a higher-order function that uses only function application and earlier defined combinators to define
+> a result from its arguments.
 
 **Programmatic Example**
 
-Translating the combinator example above. First of all, we have a interface consist of several methods `contains`, `not`, `or`, `and` .
+Translating the combinator example above. First of all, we have an interface consist of several
+methods `contains`, `not`, `or`, `and` .
 
 ```java
 // Functional interface to find lines in text.
 public interface Finder {
 
-	// The function to find lines in text.
-	List<String> find(String text);
-
-	// Simple implementation of function {@link #find(String)}.
-	static Finder contains(String word) {
-    		return txt -> Stream.of(txt.split("\n"))
-        		.filter(line -> line.toLowerCase().contains(word.toLowerCase()))
-        		.collect(Collectors.toList());
-  	}
-
-	// combinator not.
-	default Finder not(Finder notFinder) {
-    		return txt -> {
-      			List<String> res = this.find(txt);
-      			res.removeAll(notFinder.find(txt));
-      			return res;
-    			};
-  	}
-
-	// combinator or.
-	default Finder or(Finder orFinder) {
-    		return txt -> {
-      			List<String> res = this.find(txt);
-      			res.addAll(orFinder.find(txt));
-      			return res;
-    			};
-	}
-
-	// combinator and.
-	default Finder and(Finder andFinder) {
-    		return
-        	txt -> this
-            		.find(txt)
-            		.stream()
-            		.flatMap(line -> andFinder.find(line).stream())
-            		.collect(Collectors.toList());
-  	}
+    // The function to find lines in text.
+    List<String> find(String text);
+
+    // Simple implementation of function {@link #find(String)}.
+    static Finder contains(String word) {
+        return txt -> Stream.of(txt.split("\n"))
+                .filter(line -> line.toLowerCase().contains(word.toLowerCase()))
+                .collect(Collectors.toList());
+    }
+
+    // combinator not.
+    default Finder not(Finder notFinder) {
+        return txt -> {
+            List<String> res = this.find(txt);
+            res.removeAll(notFinder.find(txt));
+            return res;
+        };
+    }
+
+    // combinator or.
+    default Finder or(Finder orFinder) {
+        return txt -> {
+            List<String> res = this.find(txt);
+            res.addAll(orFinder.find(txt));
+            return res;
+        };
+    }
+
+    // combinator and.
+    default Finder and(Finder andFinder) {
+        return
+                txt -> this
+                        .find(txt)
+                        .stream()
+                        .flatMap(line -> andFinder.find(line).stream())
+                        .collect(Collectors.toList());
+    }
 	...
 }
 ```
 
-Then we have also another combinator for some complex finders `advancedFinder`, `filteredFinder`, `specializedFinder` and `expandedFinder`.
+Then we have also another combinator for some complex finders `advancedFinder`, `filteredFinder`, `specializedFinder`
+and `expandedFinder`.
 
 ```java
 // Complex finders consisting of simple finder.
 public class Finders {
 
-	private Finders() {
-  	}
-
-	// Finder to find a complex query.
-	public static Finder advancedFinder(String query, String orQuery, String notQuery) {
-    		return
-        		Finder.contains(query)
-            			.or(Finder.contains(orQuery))
-            			.not(Finder.contains(notQuery));
-	}
-
-	// Filtered finder looking a query with excluded queries as well.
-	public static Finder filteredFinder(String query, String... excludeQueries) {
-		var finder = Finder.contains(query);
-
-    		for (String q : excludeQueries) {
-      			finder = finder.not(Finder.contains(q));
-    		}
-    		return finder;
-	}
-
-	// Specialized query. Every next query is looked in previous result.
-	public static Finder specializedFinder(String... queries) {
-    		var finder = identMult();
-
-		for (String query : queries) {
-      			finder = finder.and(Finder.contains(query));
-    		}
-    		return finder;
-  	}
-
-	// Expanded query. Looking for alternatives.
-	public static Finder expandedFinder(String... queries) {
-    		var finder = identSum();
-
-    		for (String query : queries) {
-      			finder = finder.or(Finder.contains(query));
-    		}
-   		return finder;
-  	}
+    private Finders() {
+    }
+
+    // Finder to find a complex query.
+    public static Finder advancedFinder(String query, String orQuery, String notQuery) {
+        return
+                Finder.contains(query)
+                        .or(Finder.contains(orQuery))
+                        .not(Finder.contains(notQuery));
+    }
+
+    // Filtered finder looking a query with excluded queries as well.
+    public static Finder filteredFinder(String query, String... excludeQueries) {
+        var finder = Finder.contains(query);
+
+        for (String q : excludeQueries) {
+            finder = finder.not(Finder.contains(q));
+        }
+        return finder;
+    }
+
+    // Specialized query. Every next query is looked in previous result.
+    public static Finder specializedFinder(String... queries) {
+        var finder = identMult();
+
+        for (String query : queries) {
+            finder = finder.and(Finder.contains(query));
+        }
+        return finder;
+    }
+
+    // Expanded query. Looking for alternatives.
+    public static Finder expandedFinder(String... queries) {
+        var finder = identSum();
+
+        for (String query : queries) {
+            finder = finder.or(Finder.contains(query));
+        }
+        return finder;
+    }
 	...
 }
 ```
@@ -134,75 +138,102 @@ public class Finders {
 Now we have created the interface and methods for combinators. Now we have an application working on these combinators.
 
 ```java
-var queriesOr = new String[]{"many", "Annabel"};
-var finder = Finders.expandedFinder(queriesOr);
-var res = finder.find(text());
-LOGGER.info("the result of expanded(or) query[{}] is {}", queriesOr, res);
+var queriesOr=new String[]{"many","Annabel"};
+        var finder=Finders.expandedFinder(queriesOr);
+        var res=finder.find(text());
+        LOGGER.info("the result of expanded(or) query[{}] is {}",queriesOr,res);
 
-var queriesAnd = new String[]{"Annabel", "my"};
-finder = Finders.specializedFinder(queriesAnd);
-res = finder.find(text());
-LOGGER.info("the result of specialized(and) query[{}] is {}", queriesAnd, res);
+        var queriesAnd=new String[]{"Annabel","my"};
+        finder=Finders.specializedFinder(queriesAnd);
+        res=finder.find(text());
+        LOGGER.info("the result of specialized(and) query[{}] is {}",queriesAnd,res);
 
-finder = Finders.advancedFinder("it was", "kingdom", "sea");
-res = finder.find(text());
-LOGGER.info("the result of advanced query is {}", res);
+        finder=Finders.advancedFinder("it was","kingdom","sea");
+        res=finder.find(text());
+        LOGGER.info("the result of advanced query is {}",res);
 
-res = Finders.filteredFinder(" was ", "many", "child").find(text());
-LOGGER.info("the result of filtered query is {}", res);
+        res=Finders.filteredFinder(" was ","many","child").find(text());
+        LOGGER.info("the result of filtered query is {}",res);
 
-private static String text() {
-    return
+private static String text(){
+        return
         "It was many and many a year ago,\n"
-            + "In a kingdom by the sea,\n"
-            + "That a maiden there lived whom you may know\n"
-            + "By the name of ANNABEL LEE;\n"
-            + "And this maiden she lived with no other thought\n"
-            + "Than to love and be loved by me.\n"
-            + "I was a child and she was a child,\n"
-            + "In this kingdom by the sea;\n"
-            + "But we loved with a love that was more than love-\n"
-            + "I and my Annabel Lee;\n"
-            + "With a love that the winged seraphs of heaven\n"
-            + "Coveted her and me.";
-  }
+        +"In a kingdom by the sea,\n"
+        +"That a maiden there lived whom you may know\n"
+        +"By the name of ANNABEL LEE;\n"
+        +"And this maiden she lived with no other thought\n"
+        +"Than to love and be loved by me.\n"
+        +"I was a child and she was a child,\n"
+        +"In this kingdom by the sea;\n"
+        +"But we loved with a love that was more than love-\n"
+        +"I and my Annabel Lee;\n"
+        +"With a love that the winged seraphs of heaven\n"
+        +"Coveted her and me.";
+        }
 ```
 
 **Program output:**
 
 ```java
-the result of expanded(or) query[[many, Annabel]] is [It was many and many a year ago,, By the name of ANNABEL LEE;, I and my Annabel Lee;]
-the result of specialized(and) query[[Annabel, my]] is [I and my Annabel Lee;]
-the result of advanced query is [It was many and many a year ago,]
-the result of filtered query is [But we loved with a love that was more than love-]
+the result of expanded(or)query[[many,Annabel]]is[It was many and many a year ago,,By the name of ANNABEL LEE;,I and my Annabel Lee;]
+        the result of specialized(and)query[[Annabel,my]]is[I and my Annabel Lee;]
+        the result of advanced query is[It was many and many a year ago,]
+        the result of filtered query is[But we loved with a love that was more than love-]
 ```
 
-Now we can design our app to with the queries finding feature `expandedFinder`, `specializedFinder`, `advancedFinder`, `filteredFinder` which are all derived from `contains`, `or`, `not`, `and`.
-
+Now we can design our app to with the queries finding
+feature `expandedFinder`, `specializedFinder`, `advancedFinder`, `filteredFinder` which are all derived
+from `contains`, `or`, `not`, `and`.
 
 ## Class diagram
+
 ![alt text](./etc/combinator.urm.png "Combinator class diagram")
 
 ## Applicability
-Use the combinator pattern when:
 
-- You are able to create a more complex value from more plain values but having the same type(a combination of them)
+This pattern is applicable in scenarios where:
+
+* The solution to a problem can be constructed from simple, reusable components.
+* There is a need for high modularity and reusability of functions.
+* The programming environment supports first-class functions and higher-order functions.
+
+## Known Uses
+
+* Functional programming languages like Haskell and Scala extensively use combinators for tasks ranging from parsing to
+  UI construction.
+* In domain-specific languages, particularly those involved in parsing, such as parsing expression grammars.
+* In libraries for functional programming in languages like JavaScript, Python, and Ruby.
+* java.util.function.Function#compose
+* java.util.function.Function#andThen
+
+## Consequences
+
+Benefits:
 
-## Benefits
+* Enhances modularity and reusability by breaking down complex tasks into simpler, composable functions.
+* Promotes readability and maintainability by using a declarative style of programming.
+* Facilitates lazy evaluation and potentially more efficient execution through function composition.
 
-- From a developers perspective the API is made of terms from the domain.
-- There is a clear distinction between combining and application phase.
-- One first constructs an instance and then executes it.
-- This makes the pattern applicable in a parallel environment.
+Trade-offs:
 
+* Can lead to a steep learning curve for those unfamiliar with functional programming principles.
+* May result in performance overhead due to the creation of intermediate functions.
+* Debugging can be challenging due to the abstract nature of function compositions.
 
-## Real world examples
+## Related Patterns
 
-- java.util.function.Function#compose
-- java.util.function.Function#andThen
+[Strategy](https://java-design-patterns.com/patterns/strategy/): Both involve selecting an algorithm at runtime, but
+Combinator uses composition of functions.
+[Decorator](https://java-design-patterns.com/patterns/decorator/): Similar to Combinator in enhancing functionality, but
+Decorator focuses on object augmentation.
+[Chain of Responsibility](https://java-design-patterns.com/patterns/chain-of-responsibility/): Relies on chaining
+objects, whereas Combinator chains functions.
 
 ## Credits
 
-- [Example for java](https://gtrefs.github.io/code/combinator-pattern/)
+- [Example for Java](https://gtrefs.github.io/code/combinator-pattern/)
 - [Combinator pattern](https://wiki.haskell.org/Combinator_pattern)
 - [Combinatory logic](https://wiki.haskell.org/Combinatory_logic)
+- [Structure and Interpretation of Computer Programs](https://amzn.to/3PJwVsf)
+- [Functional Programming in Scala](https://amzn.to/4cEo6K2)
+- [Haskell: The Craft of Functional Programming](https://amzn.to/4axxtcF)


Test patch:
diff --git a/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java b/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java
index 4f0d42258d62..c4a505df075d 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java
@@ -22,6 +22,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+
 package com.iluwatar.combinator;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -33,12 +34,12 @@ class FinderTest {
   @Test
   void contains() {
     var example = """
-                the first one 
-                the second one\s
-                """;
+        the first one
+        the second one\s
+        """;
 
     var result = Finder.contains("second").find(example);
     assertEquals(1, result.size());
-    assertEquals( "the second one ", result.get(0));
+    assertEquals("the second one ", result.get(0));
   }
 }
diff --git a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
index bf34af1704f7..aa84b9350d5b 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
@@ -22,6 +22,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+
 package com.iluwatar.combinator;
 
 import static com.iluwatar.combinator.Finders.advancedFinder;
@@ -38,48 +39,47 @@ class FindersTest {
   void advancedFinderTest() {
     var res = advancedFinder("it was", "kingdom", "sea").find(text());
     assertEquals(1, res.size());
-    assertEquals( "It was many and many a year ago,", res.get(0));
+    assertEquals("It was many and many a year ago,", res.get(0));
   }
 
   @Test
   void filteredFinderTest() {
     var res = filteredFinder(" was ", "many", "child").find(text());
     assertEquals(1, res.size());
-    assertEquals( "But we loved with a love that was more than love-", res.get(0));
+    assertEquals("But we loved with a love that was more than love-", res.get(0));
   }
 
   @Test
   void specializedFinderTest() {
     var res = specializedFinder("love", "heaven").find(text());
     assertEquals(1, res.size());
-    assertEquals( "With a love that the winged seraphs of heaven", res.get(0));
+    assertEquals("With a love that the winged seraphs of heaven", res.get(0));
   }
 
   @Test
   void expandedFinderTest() {
     var res = expandedFinder("It was", "kingdom").find(text());
     assertEquals(3, res.size());
-    assertEquals( "It was many and many a year ago,", res.get(0));
-    assertEquals( "In a kingdom by the sea,", res.get(1));
-    assertEquals( "In this kingdom by the sea;", res.get(2));
+    assertEquals("It was many and many a year ago,", res.get(0));
+    assertEquals("In a kingdom by the sea,", res.get(1));
+    assertEquals("In this kingdom by the sea;", res.get(2));
   }
 
 
   private String text() {
-    return
-            """
-                    It was many and many a year ago,
-                    In a kingdom by the sea,
-                    That a maiden there lived whom you may know
-                    By the name of ANNABEL LEE;
-                    And this maiden she lived with no other thought
-                    Than to love and be loved by me.
-                    I was a child and she was a child,
-                    In this kingdom by the sea;
-                    But we loved with a love that was more than love-
-                    I and my Annabel Lee;
-                    With a love that the winged seraphs of heaven
-                    Coveted her and me.""";
+    return """
+        It was many and many a year ago,
+        In a kingdom by the sea,
+        That a maiden there lived whom you may know
+        By the name of ANNABEL LEE;
+        And this maiden she lived with no other thought
+        Than to love and be loved by me.
+        I was a child and she was a child,
+        In this kingdom by the sea;
+        But we loved with a love that was more than love-
+        I and my Annabel Lee;
+        With a love that the winged seraphs of heaven
+        Coveted her and me.""";
   }
 
 }