diff --git a/src/main/java/net/kemitix/mon/combinator/After.java b/src/main/java/net/kemitix/mon/combinator/After.java
new file mode 100644
index 0000000..3fd00c4
--- /dev/null
+++ b/src/main/java/net/kemitix/mon/combinator/After.java
@@ -0,0 +1,78 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Campbell
+ *
+ * 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 net.kemitix.mon.combinator;
+
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+/**
+ * After pattern combinator.
+ *
+ *
Original from http://boundsofjava.com/newsletter/003-introducing-combinators-part1
+ *
+ * @param the argument type
+ * @param the result type
+ *
+ * @author Federico Peralta Schaffner (fps@boundsofjava.com)
+ */
+@FunctionalInterface
+public interface After extends
+ Function,
+ Function<
+ BiConsumer,
+ Function>> {
+
+ /**
+ * Decorates a function with a Consumer that will be supplier with the argument before applying it to the function.
+ *
+ * @param function the function to apply the argument to and return the result value of
+ * @param after the bi-consumer that will receive the argument and the result of the function
+ * @param the argument type
+ * @param the result type
+ *
+ * @return a partially applied Function that will take an argument and return the result of applying it to the
+ * function parameter
+ */
+ static Function decorate(
+ final Function function,
+ final BiConsumer after
+ ) {
+ return After.create().apply(function)
+ .apply(after);
+ }
+
+ /**
+ * Create an After curried function.
+ *
+ * @param the argument type
+ * @param the result type
+ *
+ * @return a curried function that will pass the argument and the result of the function to the supplied bi-consumer
+ */
+ static After create() {
+ return function -> after -> argument -> {
+ final R result = function.apply(argument);
+ after.accept(argument, result);
+ return result;
+ };
+ }
+}
diff --git a/src/main/java/net/kemitix/mon/combinator/Around.java b/src/main/java/net/kemitix/mon/combinator/Around.java
new file mode 100644
index 0000000..b6d8e2e
--- /dev/null
+++ b/src/main/java/net/kemitix/mon/combinator/Around.java
@@ -0,0 +1,101 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Campbell
+ *
+ * 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 net.kemitix.mon.combinator;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+/**
+ * Around pattern combinator.
+ *
+ * Original from http://boundsofjava.com/newsletter/003-introducing-combinators-part1
+ *
+ * @param the argument type
+ * @param the result type
+ *
+ * @author Federico Peralta Schaffner (fps@boundsofjava.com)
+ */
+@FunctionalInterface
+public interface Around extends
+ Function<
+ Function,
+ Function<
+ BiConsumer, T>,
+ Function>> {
+
+ /**
+ * Decorates a function with an BiConsumer that will be supplier with an executable to perform the function, and the
+ * argument that will be applied when executed.
+ *
+ * @param function the function to apply the argument to and return the result value of
+ * @param around the bi-consumer that will supplied with the executable and the argument
+ * @param the argument type
+ * @param the result type
+ *
+ * @return a partially applied Function that will take an argument, and the result of applying it to function
+ */
+ static Function decorate(
+ final Function function,
+ final BiConsumer, T> around
+ ) {
+ return Around.create().apply(function)
+ .apply(around);
+ }
+
+ /**
+ * Create an Around curried function.
+ *
+ * @param the argument type
+ * @param the result type
+ *
+ * @return a curried function that will execute the around function, passing an executable and the invocations
+ * argument. The around function must {@code execute()} the executable and may capture the result.
+ */
+ static Around create() {
+ return function -> around -> argument -> {
+ final AtomicReference result = new AtomicReference<>();
+ final Executable callback = () -> {
+ result.set(function.apply(argument));
+ return result.get();
+ };
+ around.accept(callback, argument);
+ return result.get();
+ };
+ }
+
+ /**
+ * The executable that will be supplied to the around function to trigger the surrounded function.
+ *
+ * @param the return type of the function
+ */
+ @FunctionalInterface
+ interface Executable {
+
+ /**
+ * Executes the function.
+ *
+ * @return the result of applying the function
+ */
+ R execute();
+ }
+}
diff --git a/src/main/java/net/kemitix/mon/combinator/Before.java b/src/main/java/net/kemitix/mon/combinator/Before.java
new file mode 100644
index 0000000..4e40a29
--- /dev/null
+++ b/src/main/java/net/kemitix/mon/combinator/Before.java
@@ -0,0 +1,78 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Campbell
+ *
+ * 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 net.kemitix.mon.combinator;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Before pattern combinator.
+ *
+ * Original from http://boundsofjava.com/newsletter/003-introducing-combinators-part1
+ *
+ * @param the argument type
+ * @param the result type
+ *
+ * @author Federico Peralta Schaffner (fps@boundsofjava.com)
+ */
+@FunctionalInterface
+public interface Before extends
+ Function<
+ Consumer,
+ Function<
+ Function,
+ Function>> {
+
+ /**
+ * Decorates a function with a Consumer that will be supplied with the argument before applying it to the function.
+ *
+ * @param before the consumer that will receive the argument before the function
+ * @param function the function to apply the argument to and return the result value of
+ * @param the argument type
+ * @param the result type
+ *
+ * @return a partially applied Function that will take an argument and return the result of applying it to the
+ * function parameter
+ */
+ static Function decorate(
+ final Consumer before,
+ final Function function
+ ) {
+ return Before.create().apply(before)
+ .apply(function);
+ }
+
+ /**
+ * Create a Before curried function.
+ *
+ * @param the argument type
+ * @param the result type
+ *
+ * @return a curried function that will pass the argument to before applying the supplied function
+ */
+ static Before create() {
+ return before -> function -> argument -> {
+ before.accept(argument);
+ return function.apply(argument);
+ };
+ }
+}
diff --git a/src/main/java/net/kemitix/mon/combinator/package-info.java b/src/main/java/net/kemitix/mon/combinator/package-info.java
new file mode 100644
index 0000000..9ff7a6d
--- /dev/null
+++ b/src/main/java/net/kemitix/mon/combinator/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Campbell
+ *
+ * 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 net.kemitix.mon.combinator;
diff --git a/src/test/java/net/kemitix/mon/combinator/AfterTest.java b/src/test/java/net/kemitix/mon/combinator/AfterTest.java
new file mode 100644
index 0000000..8d5a4eb
--- /dev/null
+++ b/src/test/java/net/kemitix/mon/combinator/AfterTest.java
@@ -0,0 +1,44 @@
+package net.kemitix.mon.combinator;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AfterTest {
+
+ @Test
+ public void canCreateAfterCombinator() {
+ //given
+ final List events = new ArrayList<>();
+ final Function squareDecorated =
+ After.decorate(
+ argument -> function(argument, events),
+ (argument, result) -> after(argument, result, events)
+ );
+ //when
+ final Integer result = squareDecorated.apply(2);
+ //then
+ assertThat(result).isEqualTo(4);
+ assertThat(events).containsExactly("function", "after 2 -> 4");
+ }
+
+ private static void after(
+ final Integer argument,
+ final Integer result,
+ final List events
+ ) {
+ events.add(String.format("after %d -> %d", argument, result));
+ }
+
+ private static Integer function(
+ final Integer argument,
+ final List events
+ ) {
+ events.add("function");
+ return argument * argument;
+ }
+}
diff --git a/src/test/java/net/kemitix/mon/combinator/AroundTest.java b/src/test/java/net/kemitix/mon/combinator/AroundTest.java
new file mode 100644
index 0000000..ee94f2b
--- /dev/null
+++ b/src/test/java/net/kemitix/mon/combinator/AroundTest.java
@@ -0,0 +1,46 @@
+package net.kemitix.mon.combinator;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AroundTest {
+
+ @Test
+ public void canCreateAnAroundCombinator() {
+ //given
+ final List events = new ArrayList<>();
+ final Function squareDecorated =
+ Around.decorate(
+ argument -> function(argument, events),
+ (executable, argument) -> around(executable, argument, events)
+ );
+ //when
+ final Integer result = squareDecorated.apply(2);
+ //then
+ assertThat(result).isEqualTo(4);
+ assertThat(events).containsExactly("around before 2", "function", "around after 4");
+ }
+
+ private void around(
+ final Around.Executable executable,
+ final Integer argument,
+ final List events
+ ) {
+ events.add("around before " + argument);
+ final Integer result = executable.execute();
+ events.add("around after " + result);
+ }
+
+ private static Integer function(
+ final Integer argument,
+ final List events
+ ) {
+ events.add("function");
+ return argument * argument;
+ }
+}
diff --git a/src/test/java/net/kemitix/mon/combinator/BeforeTest.java b/src/test/java/net/kemitix/mon/combinator/BeforeTest.java
new file mode 100644
index 0000000..d14d85c
--- /dev/null
+++ b/src/test/java/net/kemitix/mon/combinator/BeforeTest.java
@@ -0,0 +1,43 @@
+package net.kemitix.mon.combinator;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BeforeTest {
+
+ @Test
+ public void canCreateBeforeCombinator() {
+ //given
+ final List events = new ArrayList<>();
+ final Function squareDecorated =
+ Before.decorate(
+ argument -> before(argument, events),
+ argument -> function(argument, events)
+ );
+ //when
+ final Integer result = squareDecorated.apply(2);
+ //then
+ assertThat(result).isEqualTo(4);
+ assertThat(events).containsExactly("before 2", "function");
+ }
+
+ private static void before(
+ final Integer argument,
+ final List events
+ ) {
+ events.add("before " + argument);
+ }
+
+ private static Integer function(
+ final Integer argument,
+ final List events
+ ) {
+ events.add("function");
+ return argument * argument;
+ }
+}