1

评论员写道:

Tomcat 中有一些不错的“大于号”代码。需要健康剂量的 (>>=)。

从 Apache Tomcat查看AuthenticatorBase.java 类时:

/**
 * 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);
                }
            }
        }
    }

我不得不承认,我错过了如何应用它。我知道有一种方法可以将 if-tree 重构为 monadic 绑定,但我不知道该怎么做。

假设:

  • 这与语言无关,而与逻辑结构有关。你可以在 Haskell、Scala 或 Clojure 中表示这个 if-tree,它仍然会表示相同的 if-logic。

我的问题是:如何使用 Monadic Bind 简化这个 Apache Tomcat 代码?

4

2 回答 2

1

好吧,这里有副作用 ( request.set...),如果没有, bind 会更有用。使用ifPresent就足够了:

Optional.ofNullable(principal).ifPresent(principal ->
  Optional.ofNullable(request.getSessionInternal(false)).ifPresent(session ->
    Optional.ofNullable(session.getPrincipal).ifPresent(principal -> {
      if (log.isDebugEnabled())
        log.debug(...);
      request.setAuthType(session.getAuthType());
      request.setUserPrincipal(principal);
    })));

这可能看起来不像是胜利;重复但如果并且已经返回Optional.ofNullable(...)则没有必要。request.getSessionInternalsession.getPrincipalOptional

您可以编写一个类似的方法Optional.ofNullable(...).ifPresent

public static <T> void ifNotNull(T value, Consumer<? super T> consumer) {
  if (value != null) { consumer.accept(value); }
}

ifNotNull(principal, principal ->
  ifNotNull(request.getSessionInternal(false), session ->
    ifNotNull(session.getPrincipal, principal -> {
      if (log.isDebugEnabled())
        log.debug(...);
      request.setAuthType(session.getAuthType());
      request.setUserPrincipal(principal);
    })));

(注意:不确定我是否准确地记得 Java 语法,我已经有一段时间没有使用它了。)

于 2015-12-24T10:29:50.810 回答
0

我是 Twitter 评论的作者。在这个例子中,为了简化,假设存在一个单子选项类型的“唾手可得的果实”,是基于foo不是null的几层嵌套代码。

让我们关注以下代码段:

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);
        }
    }
}

由于我们讨论的是一般结构,而不是专门讨论 Java 或任何特定语言,所以让我们引入一些新的假设(这些假设可能很容易在 Java 代码中体现,也可能不容易体现),我们将重构代码以利用它们。

假设1:所有可以返回的方法都null返回一个Optional<T>,并且永远不会返回null,因为缺席/失败的情况是在Optional值本身中处理的。在其他语言Optional中被称为OptionMaybe可能还有其他名称。

假设2Optional有一个单子绑定方法。Java 8Optional调用了这个函数flatMap,所以我们也会这样称呼它。在 Haskell 中,它被称为>>=,但名称并不重要——重要的是类型:

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

作为比较,在 Haskell>>=中为所有类型定义了Monad类型类的实例;类型签名是:

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

当专门用于Maybe和使用不同类型的变量名时,与 Java 的对应关系Optional变得更加清晰。

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

假设 3:该语言具有一流的 lambda。我知道 Java 8 有 lambda,但我不知道头顶的语法,所以我只是要弥补 :)

应用这些假设,简化的代码如下所示:

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);
    })
}

请注意,每次连续调用都会从程序flatMap中删除一个if级别。flatMap也可以链接调用以避免中间分配。

在 Java 中使用一元绑定模式的好处是显而易见的,但不幸的是它们是有限的。因为flatMap是具体定义的Optional(也许类似的方法也具体定义给其他类型),而不是抽象地在类型类或接口上,所以程序员无法免费访问许多派生操作。必须为每个实例手动编写此类派生操作。

于 2015-12-26T12:20:05.793 回答