diff --git a/README.org b/README.org
index 694f6d1..8117b51 100644
--- a/README.org
+++ b/README.org
@@ -119,6 +119,125 @@
}
#+END_SRC
+*** =TypeAlias= *can* be a Monad
+
+ #+BEGIN_SRC java
+ package net.kemitix.mon;
+
+ import org.assertj.core.api.WithAssertions;
+ import org.junit.Test;
+
+ import java.util.function.Function;
+
+ public class TypeAliasMonadTest implements WithAssertions {
+
+ private final int v = 1;
+ private final Function> f = i -> a(i * 2);
+ private final Function> g = i -> a(i + 6);
+
+ private static AnAlias a(Integer v) {
+ return AnAlias.of(v);
+ }
+
+ @Test
+ public void leftIdentity() {
+ assertThat(
+ a(v).flatMap(f)
+ ).isEqualTo(
+ f.apply(v)
+ );
+ }
+
+ @Test
+ public void rightIdentity_inline() {
+ // java isn't able to properly infer the correct types when used in-line
+ assertThat(
+ a(v).>flatMap(x -> a(x))
+ ).isEqualTo(
+ a(v)
+ );
+ }
+
+ @Test
+ public void rightIdentity_explicitValue() {
+ final AnAlias integerAnAlias = a(v).flatMap(x -> a(x));
+ assertThat(
+ integerAnAlias
+ ).isEqualTo(
+ a(v)
+ );
+ }
+
+ @Test
+ public void associativity() {
+ assertThat(
+ a(v).flatMap(f).flatMap(g)
+ ).isEqualTo(
+ a(v).flatMap(x -> f.apply(x).flatMap(g))
+ );
+ }
+
+ static class AnAlias extends TypeAlias {
+ private AnAlias(T value) {
+ super(value);
+ }
+
+ static AnAlias of(T value) {
+ return new AnAlias<>(value);
+ }
+ }
+ }
+ #+END_SRC
+
+
+*** Instance Methods
+
+**** =final R map(final Function f)=
+
+ Map the TypeAlias into another value.
+
+ #+BEGIN_SRC java
+ final StudentId studentId = StudentId.of(123);
+ final String idString = studentId.map(id -> String.valueOf(id));
+
+ class StudentId extends TypeAlias {
+ private StudentId(Integer value) {
+ super(value);
+ }
+ static StudentId of(Integer id) {
+ return new StudentId(id);
+ }
+ }
+ #+END_SRC
+
+
+**** =final > U flatMap(final Function f)=
+
+ Map the TypeAlias into another TypeAlias.
+
+ #+BEGIN_SRC java
+ final StudentId studentId = StudentId.of(123);
+ final StudentName studentName = studentId.flatMap(id -> getStudentName(id));
+
+ class StudentName extends TypeAlias {
+ private StudentName(String value) {
+ super(value);
+ }
+ static StudentName of(final String name) {
+ return new StudentName(name);
+ }
+ }
+ #+END_SRC
+
+
+**** =T getValue()=
+
+ Get the value of the TypeAlias.
+
+ #+BEGIN_SRC java
+ final String name = studentName.getValue();
+ #+END_SRC
+
** Maybe
@@ -734,7 +853,7 @@
convention, used to indicate the error, and right the success. An alternative
is to use the =Result= which more clearly distinguishes success from failure.
- =Either= is not a Monad.
+*** =Either= *is not* a Monad.
*** Static Constructors
@@ -746,6 +865,7 @@
final Either left = Either.left(getIntegerValue());
#+END_SRC
+
**** =static Either right(final R r)=
Create a new Either holding a right value.
@@ -766,6 +886,7 @@
final boolean rightIsLeft = Either.right(getStringValue()).isLeft();
#+END_SRC
+
**** =boolean isRight()=
Checks if the Either holds a right value.
@@ -788,6 +909,7 @@
);
#+END_SRC
+
**** = Either mapLeft(Function f)=
Map the function across the left value.
@@ -797,6 +919,7 @@
.mapLeft(i -> i.doubleValue());
#+END_SRC
+
**** = Either mapRight(Function f)=
Map the function across the right value.
@@ -805,3 +928,5 @@
final Either either = Either.left(getIntegerValue())
.mapRight(s -> s + "x");
#+END_SRC
+
+
diff --git a/src/main/java/net/kemitix/mon/TypeAlias.java b/src/main/java/net/kemitix/mon/TypeAlias.java
index 5d49978..d4df4ef 100644
--- a/src/main/java/net/kemitix/mon/TypeAlias.java
+++ b/src/main/java/net/kemitix/mon/TypeAlias.java
@@ -30,7 +30,6 @@ import java.util.function.Function;
* for the type being aliased.
*
* @param the type of the alias
- *
* @author Paul Campbell (pcampbell@kemitix.net)
*/
@SuppressWarnings("abstractclassname")
@@ -55,13 +54,24 @@ public abstract class TypeAlias {
*
* @param f the function to create the new value
* @param the type of the new value
- *
- * @return a TypeAlias
+ * @return the result of the function
*/
public final R map(final Function f) {
return f.apply(value);
}
+ /**
+ * Map the TypeAlias into another TypeAlias.
+ *
+ * @param f the function to create the new value
+ * @param the type of the new value within a TypeAlias
+ * @param the type of the TypeAlias superclass containing the new value
+ * @return a TypeAlias
+ */
+ public final > U flatMap(final Function f) {
+ return f.apply(value);
+ }
+
@Override
public final int hashCode() {
return value.hashCode();
@@ -73,7 +83,7 @@ public abstract class TypeAlias {
final TypeAlias other = (TypeAlias) o;
final Class> otherValueClass = other.value.getClass();
return otherValueClass.equals(getValue().getClass())
- && other.value.equals(getValue());
+ && other.value.equals(getValue());
}
return map(o::equals);
}
diff --git a/src/test/java/net/kemitix/mon/TypeAliasMonadTest.java b/src/test/java/net/kemitix/mon/TypeAliasMonadTest.java
new file mode 100644
index 0000000..90c87df
--- /dev/null
+++ b/src/test/java/net/kemitix/mon/TypeAliasMonadTest.java
@@ -0,0 +1,55 @@
+package net.kemitix.mon;
+
+import org.assertj.core.api.WithAssertions;
+import org.junit.Test;
+
+import java.util.function.Function;
+
+public class TypeAliasMonadTest implements WithAssertions {
+
+ private final int v = 1;
+ private final Function> f = i -> a(i * 2);
+ private final Function> g = i -> a(i + 6);
+
+ private static AnAlias a(Integer v) {
+ return AnAlias.of(v);
+ }
+
+ @Test
+ public void leftIdentity() {
+ assertThat(
+ a(v).flatMap(f)
+ ).isEqualTo(
+ f.apply(v)
+ );
+ }
+
+ @Test
+ public void rightIdentity() {
+ final AnAlias integerAnAlias = a(v).flatMap(x -> a(x));
+ assertThat(
+ integerAnAlias
+ ).isEqualTo(
+ a(v)
+ );
+ }
+
+ @Test
+ public void associativity() {
+ assertThat(
+ a(v).flatMap(f).flatMap(g)
+ ).isEqualTo(
+ a(v).flatMap(x -> f.apply(x).flatMap(g))
+ );
+ }
+
+ static class AnAlias extends TypeAlias {
+ private AnAlias(T value) {
+ super(value);
+ }
+
+ static AnAlias of(T value) {
+ return new AnAlias<>(value);
+ }
+ }
+}