B foo(A a) { /* code */ }
C bar(B b) { /* code */ }
bar(foo(someA))
import java.util.function.Function;
public interface Monad<T> {
<U> Monad<U> map(Function<T, U> mapper);
T get(); // unwrap value
}
public class Identity<T> implements Monad<T> {
private T value;
private Identity(T value) { this.value = value; }
// for a Static Factory, of() is a
// better name than unit()
public static <T> Identity<T> of(T value) {
return new Identity<T>(value);
}
// map can act as a decorator of mapper
public <U> Identity<U> map(Function<T, U> mapper) {
U mapperValue = mapper.apply(value);
return Identity.of(mapperValue); // can't escape the Monad!
}
public T get() {
return value;
}
}
System.out.println(
Identity.of("Monads are fun")
.map(String::toUppercase)
.map(s -> s.replace(" ", "-"))
.get()
); // prints MONADS-ARE-FUN
"Monads are Fun".toUppercase().replace(" ", "-");
System.out.println(
Identity.of("Monads are fun") // <String>
.map(s -> s.replace(" ", ""))
.map(String::length) // <Integer>
.get();
); // prints 12
"Monads are Fun".replace(" ", "").length();
public class Identity<T> implements Monad<T> {
private T value;
private Identity(T value) { this.value = value; }
public static <T> Identity<T> of(T value) {
return new Identity<T>(value);
}
public <U> Identity<U> map(Function<T, U> mapper) {
U mapperValue = mapper.apply(value);
return Identity.of(mapperValue); // can't escape the Monad!
}
// mapper already returns an Identity
public <U> Identity<U> flatMap(Function<T, Identity<U>> mapper) {
return mapper.apply(value); // no need to wrap returned value
}
public T get() {
return value;
}
}
String title = Optional.of(page)
.map(Page::getTitle)
.orElse(StringUtils.EMPTY);
String subtitle = Optional.of(page)
.map(Page::getSubtitle)
.orElseGet(() -> page.getSummary());
Optional<Event> nextEvent = Optional.of(events)
.filter(list -> list.size() > 0)
.map(list -> list.get(0));
Optional.of(allArticles)
.filter(articles -> articles.size() > 0)
.map(articles -> articles.get(0))
.map(Article::from)
.orElse(null);
Optional.of(page.getContentResource())
.map(content -> content.getChild("likes"))
.map(Resource::getValueMap)
.map(likesProperties -> likesProperties.containsKey(id))
.orElse(false);
Optional.ofNullable(node)
.filter(n -> n.hasProperty("foo"))
.map(n -> n.getProperty("foo"))
.map(Property::getString)
.orElse(StringUtils.EMPTY);
Use Optional.ofNullable if the value
you want to wrap could be null
properties.put("shortsummary", description);
properties.put("type", type);
properties.put("groupName", urlName);
Optional.ofNullable(tags)
.ifPresent(t -> properties.put("tags", t);
We can avoid if (tags != null) and write
code that while not shorter is simpler
Arrays.stream(values)
.filter(Tag::valid)
.findFirst() // returns an Optional
.isPresent();
Some Streams operations
return an Optional
Stream.of(attributes)
.filter(attribute -> StringUtils.startsWith(attribute, UID_KEY))
.findFirst()
.map(attribute -> attribute.replace(UID_KEY, StringUtils.EMPTY))
.orElse(supervisorId);
So you can move from
Streams to Optional naturally
// code
try {
} catch (SomeException e) {
}
// code
try {
} catch (OtherException e) {
}
// code
try {
// code
try {
} catch (OtherException e) {
}
// code
} catch (SomeException e) {
}
// code
// code
try {
} catch (SomeException e) {
// code
try {
} catch (OtherException e) {
}
// code
}
// code
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.9.0</version>
</dependency>
Try.of(() -> node.getPath())
.getOrElse(StringUtils.EMPTY);
Try.of(() -> sourceNode.getPath())
.map(Colors::getColorByPath)
.getOrElse(EMPTY);
Try.of(() -> Node.class.cast(source))
.mapTry(parent -> parent.getNode("content"))
.map(FileModel::hasMetadata)
.getOrElse(false);
Try.of(() -> node.getPath())
.getOrElseThrow(e -> new AssertionError(e));
Try.of(() -> node.getPath())
.onFailure(e -> LOGGER.error(e))
.getOrElse(StringUtils.EMPTY);
Try.run(() -> {
response.setStatus(HttpStatus.SC_OK);
response.getWriter().write("Foo");
}).onFailure(e -> LOGGER.error("Can't send message to client", e));
public void doHead(HttpServletRequest request, HttpServletResponse response) {
//retrieve the requested URL
String uri = request.getParameter("uri");
//obtain the session from the request
Try.of(() -> request.getResourceResolver().adaptTo(Session.class))
.andThen(session -> Try.run(() -> {
session.checkPermission(uri, Session.ACTION_READ);
LOGGER.debug("authchecker says OK");
response.setStatus(SlingHttpServletResponse.SC_OK);
})
.onFailure(e -> {
LOGGER.error("authchecker says READ access DENIED!");
response.setStatus(SlingHttpServletResponse.SC_FORBIDDEN);
})
.onFailure(e -> LOGGER.error("authchecker servlet exception: {}", e.getMessage())));
}
Try.of(() -> request.getSession())
.andThen(session -> Try.run(() -> checkPermission(uri, session, response))
.onFailure(e -> setForbiddenStatus(response))
.onFailure(e -> LOGGER.error(e));
private void checkPermission(String uri, Session session,
SlingHttpServletResponse response) throws RepositoryException {
session.checkPermission(uri, Session.ACTION_READ);
LOGGER.debug("authchecker says OK");
response.setStatus(SlingHttpServletResponse.SC_OK);
}
private void setForbiddenStatus(SlingHttpServletResponse response) {
LOGGER.error("authchecker says READ access DENIED!");
response.setStatus(SlingHttpServletResponse.SC_FORBIDDEN);
}
Optional.of(node)
.filter(myNode ->
Try.of(() -> myNode.hasNode("content"))
.getOrElse(false))
.map(myNode ->
Try.of(() -> myNode.getNode("content"))
.getOrElse((Node) null));
Since lambdas can't throw exceptions you can use Try to mitigate this problem
public void foo()
throws FileNotFoundException, XMLParsingException {
}
I know exactly what exceptions will be thrown
and I can't ignore them
unless I really try to do it;
this reduces the chance
of bad exception handling
public Try<Void> foo() {}
I don't know how many exceptions can happen and nor of what type. I can silently ignore to handle them and
the compiler won't complain
public long calculateDebt(List<User> users) {
return users.stream()
.filter(user -> user.type == PREMIUM)
.sort(Compator.comparingLong(User::debt))
.limit(50)
.sum();
}
public List<Event> upcoming(final List<Node> nodes) {
LocalDate now = LocalDate.now();
return nodes.stream()
.sorted(COMPARE_BY_EVENT_DATE)
.map(Event::from)
.filter(Event::isConfigured)
.filter(event -> event.getDate().compareTo(now) >= 0)
.collect(Collectors.toList());
}
public final class Foo {
public static Foo bar() {
return null; // always return null
}
public static String neverDoThis(Foo foo) {
if (foo == null) { // handle null, that's wrong!
return "";
}
return "!!!";
}
}
public final class Main {
public static void main(String[] args) {
// traditional composition
Integer value = Foo.dontDoThis(Foo.bar());
// first return null, then handles
// null as a special case!
// Monadic composition
Optional<Integer> monad = Optional.ofNullable(Foo.bar())
// Optional is empty
.map(Foo::dontDoThis);
// This is ignored
System.out.println(monad.isPresent());
// false after ofNullable it got empty
System.out.println(value != null);
// true! is not "empty" without the Monad
}
}
package code.cobregon.util;
import code.acme.util.Optional; // my current choice
public class Optional<T> {
private code.cobregon.util.Optional wrapped;
private Optional(T value) {
wrapped = code.cobregon.util.Optional.of(value);
}
public static Optional<T> of(T value) {
return new Optional(value);
}
public <U> Optional<U> map(Function<T, U> mapper) {
return wrapped.map(mapper);
}
// all methods you want to expose; they should use delegation
}