Repository: iluwatar/java-design-patterns
Issue: Monad pattern
Labels: epic: pattern

It is described in this talk: https://youtu.be/-k2X7guaArU


Issue comments:
One more useful link: https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads

A rather detailed discussion explaining the fundamentals of monads.

I will work on that issue tomorrow 

Ok @Crossy147 I will update the issue tags.


Pull request review comments:

Patch that resolved the issue:
diff --git a/monad/etc/monad.ucls b/monad/etc/monad.ucls
new file mode 100644
index 000000000000..9c98df7c6331
--- /dev/null
+++ b/monad/etc/monad.ucls
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<class-diagram version="1.1.9" icons="true" always-add-relationships="false" generalizations="true" realizations="true" 
+  associations="true" dependencies="false" nesting-relationships="true" router="FAN">  
+  <class id="1" language="java" name="com.iluwatar.monad.App" project="monad" 
+    file="/monad/src/main/java/com/iluwatar/monad/App.java" binary="false" corner="BOTTOM_RIGHT">    
+    <position height="101" width="125" x="631" y="37"/>    
+    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+      sort-features="false" accessors="true" visibility="true">      
+      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
+      <operations public="true" package="true" protected="true" private="true" static="true"/>    
+    </display>  
+  </class>  
+  <enumeration id="2" language="java" name="com.iluwatar.monad.Sex" project="monad" 
+    file="/monad/src/main/java/com/iluwatar/monad/Sex.java" binary="false" corner="BOTTOM_RIGHT">    
+    <position height="119" width="137" x="424" y="286"/>    
+    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+      sort-features="false" accessors="true" visibility="true">      
+      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
+      <operations public="true" package="true" protected="true" private="true" static="true"/>    
+    </display>  
+  </enumeration>  
+  <class id="3" language="java" name="com.iluwatar.monad.User" project="monad" 
+    file="/monad/src/main/java/com/iluwatar/monad/User.java" binary="false" corner="BOTTOM_RIGHT">    
+    <position height="209" width="167" x="424" y="37"/>    
+    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+      sort-features="false" accessors="true" visibility="true">      
+      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
+      <operations public="true" package="true" protected="true" private="true" static="true"/>    
+    </display>  
+  </class>  
+  <class id="4" language="java" name="com.iluwatar.monad.Validator" project="monad" 
+    file="/monad/src/main/java/com/iluwatar/monad/Validator.java" binary="false" corner="BOTTOM_RIGHT">    
+    <position height="191" width="343" x="41" y="37"/>    
+    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+      sort-features="false" accessors="true" visibility="true">      
+      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
+      <operations public="true" package="true" protected="true" private="true" static="true"/>    
+    </display>  
+  </class>  
+  <association id="5">    
+    <end type="SOURCE" refId="3" navigable="false">      
+      <attribute id="6" name="sex"/>      
+      <multiplicity id="7" minimum="0" maximum="1"/>    
+    </end>    
+    <end type="TARGET" refId="2" navigable="true"/>    
+    <display labels="true" multiplicity="true"/>  
+  </association>  
+  <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+    sort-features="false" accessors="true" visibility="true">    
+    <attributes public="true" package="true" protected="true" private="true" static="true"/>    
+    <operations public="true" package="true" protected="true" private="true" static="true"/>  
+  </classifier-display>  
+  <association-display labels="true" multiplicity="true"/>
+</class-diagram>
\ No newline at end of file
diff --git a/monad/index.md b/monad/index.md
new file mode 100644
index 000000000000..82deba9222a6
--- /dev/null
+++ b/monad/index.md
@@ -0,0 +1,35 @@
+---
+layout: pattern
+title: Monad
+folder: monad
+permalink: /patterns/monad/
+categories: Other
+tags:
+ - Java
+ - Difficulty-Advanced
+ - Functional
+---
+
+## Intent
+
+Monad pattern based on monad from linear algebra represents the way of chaining operations
+together step by step. Binding functions can be described as passing one's output to another's input
+basing on the 'same type' contract. Formally, monad consists of a type constructor M and two
+operations:
+bind - that takes monadic object and a function from plain object to monadic value and returns monadic value
+return - that takes plain type object and returns this object wrapped in a monadic value.
+
+![alt text](./etc/monad.png "Monad")
+
+## Applicability
+
+Use the Monad in any of the following situations
+
+* when you want to chain operations easily
+* when you want to apply each function regardless of the result of any of them
+
+## Credits
+
+* [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU)
+* [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads)
+* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming))
\ No newline at end of file
diff --git a/monad/pom.xml b/monad/pom.xml
new file mode 100644
index 000000000000..ec5a14a8c19c
--- /dev/null
+++ b/monad/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+
+    The MIT License
+    Copyright (c) 2014 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.
+
+-->
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.iluwatar</groupId>
+        <artifactId>java-design-patterns</artifactId>
+        <version>1.11.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>monad</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
+
diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java
new file mode 100644
index 000000000000..e330cfa64bfc
--- /dev/null
+++ b/monad/src/main/java/com/iluwatar/monad/App.java
@@ -0,0 +1,35 @@
+package com.iluwatar.monad;
+
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * The Monad pattern defines a monad structure, that enables chaining operations
+ * in pipelines and processing data step by step.
+ * Formally, monad consists of a type constructor M and two operations:
+ * <br>bind - that takes monadic object and a function from plain object to the
+ * monadic value and returns monadic value.
+ * <br>return - that takes plain type object and returns this object wrapped in a monadic value.
+ * <p>
+ * In the given example, the Monad pattern is represented as a {@link Validator} that takes an instance
+ * of a plain object with {@link Validator#of(Object)}
+ * and validates it {@link Validator#validate(Function, Predicate, String)} against given predicates.
+ * <p>As a validation result {@link Validator#get()} it either returns valid object {@link Validator#t}
+ * or throws a list of exceptions {@link Validator#exceptions} collected during validation.
+ */
+public class App {
+
+  /**
+   * Program entry point.
+   *
+   * @param args @param args command line args
+   */
+  public static void main(String[] args) {
+    User user = new User("user", 24, Sex.FEMALE, "foobar.com");
+    System.out.println(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null")
+        .validate(User::getName, name -> !name.isEmpty(), "name is empty")
+        .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'")
+        .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString());
+  }
+}
diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java
new file mode 100644
index 000000000000..8b7e43f3ccf3
--- /dev/null
+++ b/monad/src/main/java/com/iluwatar/monad/Sex.java
@@ -0,0 +1,5 @@
+package com.iluwatar.monad;
+
+public enum Sex {
+  MALE, FEMALE
+}
diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java
new file mode 100644
index 000000000000..edd29964327a
--- /dev/null
+++ b/monad/src/main/java/com/iluwatar/monad/User.java
@@ -0,0 +1,38 @@
+package com.iluwatar.monad;
+
+public class User {
+
+  private String name;
+  private int age;
+  private Sex sex;
+  private String email;
+
+  /**
+   * @param name  - name
+   * @param age   - age
+   * @param sex   - sex
+   * @param email - email address
+   */
+  public User(String name, int age, Sex sex, String email) {
+    this.name = name;
+    this.age = age;
+    this.sex = sex;
+    this.email = email;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public int getAge() {
+    return age;
+  }
+
+  public Sex getSex() {
+    return sex;
+  }
+
+  public String getEmail() {
+    return email;
+  }
+}
diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java
new file mode 100644
index 000000000000..2298a4d8e324
--- /dev/null
+++ b/monad/src/main/java/com/iluwatar/monad/Validator.java
@@ -0,0 +1,91 @@
+package com.iluwatar.monad;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Class representing Monad design pattern. Monad is a way of chaining operations on the
+ * given object together step by step. In Validator each step results in either success or
+ * failure indicator, giving a way of receiving each of them easily and finally getting
+ * validated object or list of exceptions.
+ *
+ * @param <T> Placeholder for an object.
+ */
+public class Validator<T> {
+  /**
+   * Object that is validated
+   */
+  private final T t;
+
+  /**
+   * List of exception thrown during validation.
+   */
+  private final List<Throwable> exceptions = new ArrayList<>();
+
+  /**
+   * Creates a monadic value of given object.
+   * @param t object to be validated
+   */
+  private Validator(T t) {
+    this.t = t;
+  }
+
+  /**
+   * Creates validator against given object
+   *
+   * @param t   object to be validated
+   * @param <T> object's type
+   * @return new instance of a validator
+   */
+  public static <T> Validator<T> of(T t) {
+    return new Validator<>(Objects.requireNonNull(t));
+  }
+
+  /**
+   * @param validation one argument boolean-valued function that
+   *                   represents one step of validation. Adds exception to main validation exception
+   *                   list when single step validation ends with failure.
+   * @param message    error message when object is invalid
+   * @return this
+   */
+  public Validator<T> validate(Predicate<T> validation, String message) {
+    if (!validation.test(t)) {
+      exceptions.add(new IllegalStateException(message));
+    }
+    return this;
+  }
+
+  /**
+   * Extension for the {@link Validator#validate(Function, Predicate, String)} method,
+   * dedicated for objects, that need to be projected before requested validation.
+   *
+   * @param projection function that gets an objects, and returns projection representing
+   *                   element to be validated.
+   * @param validation see {@link Validator#validate(Function, Predicate, String)}
+   * @param message    see {@link Validator#validate(Function, Predicate, String)}
+   * @param <U>        see {@link Validator#validate(Function, Predicate, String)}
+   * @return this
+   */
+  public <U> Validator<T> validate(Function<T, U> projection, Predicate<U> validation,
+                                   String message) {
+    return validate(projection.andThen(validation::test)::apply, message);
+  }
+
+  /**
+   * Receives validated object or throws exception when invalid.
+   *
+   * @return object that was validated
+   * @throws IllegalStateException when any validation step results with failure
+   */
+  public T get() throws IllegalStateException {
+    if (exceptions.isEmpty()) {
+      return t;
+    }
+    IllegalStateException e = new IllegalStateException();
+    exceptions.forEach(e::addSuppressed);
+    throw e;
+  }
+}
diff --git a/pom.xml b/pom.xml
index c15ca20cfb1c..0b587fd06133 100644
--- a/pom.xml
+++ b/pom.xml
@@ -121,6 +121,7 @@
 		<module>event-driven-architecture</module>
 		<module>feature-toggle</module>
 		<module>value-object</module>
+		<module>monad</module>
 	</modules>
 
 	<dependencyManagement>


Test patch:
diff --git a/monad/src/test/java/com/iluwatar/monad/AppTest.java b/monad/src/test/java/com/iluwatar/monad/AppTest.java
new file mode 100644
index 000000000000..5d23b41a6759
--- /dev/null
+++ b/monad/src/test/java/com/iluwatar/monad/AppTest.java
@@ -0,0 +1,13 @@
+package com.iluwatar.monad;
+
+import org.junit.Test;
+
+public class AppTest {
+
+  @Test
+  public void testMain() {
+    String[] args = {};
+    App.main(args);
+  }
+
+}
diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java
new file mode 100644
index 000000000000..ae78572f87f7
--- /dev/null
+++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java
@@ -0,0 +1,42 @@
+package com.iluwatar.monad;
+
+
+import junit.framework.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Objects;
+
+public class MonadTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void testForInvalidName() {
+    thrown.expect(IllegalStateException.class);
+    User tom = new User(null, 21, Sex.MALE, "tom@foo.bar");
+    Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null").get();
+  }
+
+  @Test
+  public void testForInvalidAge() {
+    thrown.expect(IllegalStateException.class);
+    User john = new User("John", 17, Sex.MALE, "john@qwe.bar");
+    Validator.of(john).validate(User::getName, Objects::nonNull, "name cannot be null")
+        .validate(User::getAge, age -> age > 21, "user is underaged")
+        .get();
+  }
+
+  @Test
+  public void testForValid() {
+    User sarah = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org");
+    User validated = Validator.of(sarah).validate(User::getName, Objects::nonNull, "name cannot be null")
+        .validate(User::getAge, age -> age > 21, "user is underaged")
+        .validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female")
+        .validate(User::getEmail, email -> email.contains("@"), "email does not contain @ sign")
+        .get();
+    Assert.assertSame(validated, sarah);
+  }
+}