如何使用 Monadic Bind 简化此 Apache Tomcat 代码?
How could this Apache Tomcat code be simplified with a Monadic Bind?
Some nice "greater-than sign" code in Tomcat. Needs a healthy dose of (>>=).
查看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);
}
}
}
}
我不得不承认,我不知道如何应用它。我知道有一种方法可以将 if-tree 重构为 monadic 绑定,但我不知道该怎么做。
假设:
- 这不是关于语言,而是关于逻辑构造。您可以在 Haskell 或 Scala 或 Clojure 中表示此 if-tree,它仍然表示相同的 if-logic。
我的问题是:如何使用 Monadic Bind 简化此 Apache Tomcat 代码?
好吧,这里有副作用 (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);
})));
这看起来不像是胜利;重复,但如果 request.getSessionInternal
和 session.getPrincipal
已经返回 Optional
,则 Optional.ofNullable(...)
不是必需的。
你可以编写一个方法,其工作方式类似于 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 语法,我已经有一段时间没有使用它了。)
我是 Twitter 评论的作者。在此示例中,"low-hanging fruit" 用于
简化,假设存在一个单子选项类型,是在“foo
上预测的几层嵌套代码不是null
” =68=].
让我们关注以下片段:
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:所有可以 return null
return 和 Optional<T>
的方法,并且 从不 return null
因为 absense/failure 情况是在 Optional
值本身中处理的。在其他语言中,Optional
被称为 Option
、Maybe
,可能还有其他名称。
假设 2:Optional
有一个 monadic bind 方法。 Java 8 的 Optional
调用此函数 flatMap
,因此我们也将调用它。在 Haskell 中,它被称为 >>=
,但名称并不重要 - 重要的是类型:
<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)
为了比较,在 Haskell 中,>>=
是为所有具有 Monad
类型 class 实例的类型定义的;类型签名是:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
当专用于Maybe
并使用不同的类型变量名时,与Java的Optional
的对应关系变得更加清晰。
(>>=) :: Maybe t -> (t -> Maybe u) -> Maybe u
假设 3:该语言具有 first-class 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
具体定义的(也许类似的方法也为其他类型具体定义),而不是抽象地定义在类型 class 或接口上,程序员无权访问免费的许多派生操作。必须为每个实例手动编写此类派生操作。
Some nice "greater-than sign" code in Tomcat. Needs a healthy dose of (>>=).
查看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);
}
}
}
}
我不得不承认,我不知道如何应用它。我知道有一种方法可以将 if-tree 重构为 monadic 绑定,但我不知道该怎么做。
假设:
- 这不是关于语言,而是关于逻辑构造。您可以在 Haskell 或 Scala 或 Clojure 中表示此 if-tree,它仍然表示相同的 if-logic。
我的问题是:如何使用 Monadic Bind 简化此 Apache Tomcat 代码?
好吧,这里有副作用 (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);
})));
这看起来不像是胜利;重复,但如果 request.getSessionInternal
和 session.getPrincipal
已经返回 Optional
,则 Optional.ofNullable(...)
不是必需的。
你可以编写一个方法,其工作方式类似于 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 语法,我已经有一段时间没有使用它了。)
我是 Twitter 评论的作者。在此示例中,"low-hanging fruit" 用于
简化,假设存在一个单子选项类型,是在“foo
上预测的几层嵌套代码不是null
” =68=].
让我们关注以下片段:
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:所有可以 return null
return 和 Optional<T>
的方法,并且 从不 return null
因为 absense/failure 情况是在 Optional
值本身中处理的。在其他语言中,Optional
被称为 Option
、Maybe
,可能还有其他名称。
假设 2:Optional
有一个 monadic bind 方法。 Java 8 的 Optional
调用此函数 flatMap
,因此我们也将调用它。在 Haskell 中,它被称为 >>=
,但名称并不重要 - 重要的是类型:
<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)
为了比较,在 Haskell 中,>>=
是为所有具有 Monad
类型 class 实例的类型定义的;类型签名是:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
当专用于Maybe
并使用不同的类型变量名时,与Java的Optional
的对应关系变得更加清晰。
(>>=) :: Maybe t -> (t -> Maybe u) -> Maybe u
假设 3:该语言具有 first-class 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
具体定义的(也许类似的方法也为其他类型具体定义),而不是抽象地定义在类型 class 或接口上,程序员无权访问免费的许多派生操作。必须为每个实例手动编写此类派生操作。