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 をモナド バインドにリファクタリングする方法がある可能性があることはわかりましたが、その方法がわかりません。

仮定:

  • これは言語に関するものではなく、論理構造に関するものです。この if-tree を Haskell、Scala、または Clojure で表すことができ、それでも同じ if-logic を表すことになります。

私の質問は、この Apache Tomcat コードを Monadic Bind でどのように単純化できるでしょうか?

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のコメントを書いた者です。この例では、モナドオプション typeが存在すると仮定した場合の単純化のための「容易に達成できる成果」は、 foois not 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 : 返される可能性のあるすべてのメソッドは代わりにnullanを返し、不在/失敗のケースは値自体で処理されるため、決して返されません。他の言語では、さまざまに呼ばれ、場合によっては別の名前が付けられます。Optional<T>nullOptionalOptionalOptionMaybe

仮定 2 :モナドの bindメソッドOptionalがある。Java 8はこの関数を呼び出すので、それも同様に呼び出します。Haskell では と呼ばれますが、名前は関係ありません。重要なのは型です。OptionalflatMap>>=

<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 : 言語にはファーストクラスのラムダがあります。Java 8 にはラムダがあることは知っていますが、頭のてっぺんの構文はわかりません。

これらの仮定を適用すると、簡略化されたコードは次のようになります。

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 に答える