hawkeye hawkeye - 4 years ago 190
Java Question

How could this Apache Tomcat code be simplified with a Monadic Bind?

The commentator writes:


Some nice "greater-than sign" code in Tomcat. Needs a healthy dose of (>>=).


When looking at the AuthenticatorBase.java class from Apache Tomcat:

/**
* Enforce the security restrictions in the web application deployment
* descriptor of our associated Context.
*
* @param request Request to be processed
* @param response Response to be processed
*
* @exception IOException if an input/output error occurs
* @exception ServletException if thrown by a processing element
*/
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException {

if (log.isDebugEnabled())
log.debug("Security checking request " +
request.getMethod() + " " + request.getRequestURI());
LoginConfig config = this.context.getLoginConfig();

// Have we got a cached authenticated Principal to record?
if (cache) {
Principal principal = request.getUserPrincipal();
if (principal == null) {
Session session = request.getSessionInternal(false);
if (session != null) {
principal = session.getPrincipal();
if (principal != null) {
if (log.isDebugEnabled())
log.debug("We have cached auth type " +
session.getAuthType() +
" for principal " +
session.getPrincipal());
request.setAuthType(session.getAuthType());
request.setUserPrincipal(principal);
}
}
}
}


I have to admit, I'm missing how this could be applied. I get that there is potentially a way to refactor an if-tree to a monadic bind, but I don't see how to do it.

Assumptions:


  • This is not about language, but about the logic construct. You could represent this if-tree in Haskell or Scala or Clojure and it would still be representing the same if-logic.



My question is: How could this Apache Tomcat code be simplified with a Monadic Bind?

Answer Source

I am the author of the Twitter comment. In this example the "low-hanging fruit" for simplification, assuming the existence of a monadic option type, is the several layers of nested code predeciated on "foo is not null".

Let us focus on the following snippet:

Principal principal = request.getUserPrincipal();
if (principal == null) {
    Session session = request.getSessionInternal(false);
    if (session != null) {
        principal = session.getPrincipal();
        if (principal != null) {
            if (log.isDebugEnabled())
                log.debug("We have cached auth type " +
                    session.getAuthType() +
                    " for principal " +
                    session.getPrincipal());
            request.setAuthType(session.getAuthType());
            request.setUserPrincipal(principal);
        }
    }
}

Since we are talking about general constructions rather Java specifically, or any particular language, let's introduce some new assumptions (which may or may not be easily embodied in Java code), and we will refactor the code to exploit them.

Assumption 1: all methods that could return null return an Optional<T> instead, and never return null because the absense/failure case is handled in the Optional value itself. In other languages Optional is variously called Option, Maybe and possibly other names.

Assumption 2: Optional has a monadic bind method. Java 8's Optional calls this function flatMap, so we will call it that as well. In Haskell, it is called >>=, but the name does not matter - what matters is the type:

<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)

For comparison, in Haskell >>= is defined for all types that have an instance of the Monad type class; the type signature being:

(>>=) :: Monad m => m a -> (a -> m b) -> m b

When specialised to Maybe and using different type variable names, the correspondence to Java's Optional becomes clearer.

(>>=) :: Maybe t -> (t -> Maybe u) -> Maybe u

Assumption 3: the language has first-class lambdas. I know that Java 8 has lambdas but I do not know the syntax of the top of my head, so I'm just going to make it up :)

Applying these assumptions, the simplified code looks something like:

Optional<Principal> principal = request.getUserPrincipal();
if (!principal.isPresent()) {
    Optional<Session> session = request.getSessionInternal(false);
    principal = session.flatMap((sess) { sess.getPrincipal() });
    principal.flatMap((principal) {
        if (log.isDebugEnabled()) ... // as before
        request.setAuthType(session.getAuthType());
        request.setUserPrincipal(principal);
    })
}

Notice that each successive call to flatMap removes an if level from the program. The calls to flatMap can also be chained to avoid intermediate assignments.

The benefits to using a monadic bind pattern in Java are apparent, but they are unfortunately limited. Because flatMap is definined concretely for Optional (perhaps a similar method is also defined concretely for other types), rather than abstractly over a type class or interface, the programmer does not have access to the many derived operations for free. Such derived operations must be manually written for each instance.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download