Scala for = Haskell do (Java の例外を Either で包んだ後の話し)

最近、こっそりと RabbitMQ Java ClientScala Wrapper を書いています。
Scala から Java のライブラリを使う場合、例外処理の扱いに困るのですが、当然、RabbitMQ Java Client も例外投げまくりです。
そこで scala.util.control.Exception.allCatch を使って例外を包んでみました。*1

  class ConnectionFactory (factory: MQFactory) {
    def connect(): Either[Throwable, Connection] = allCatch either {
      new Connection(factory.newConnection)
    }
  }

  object ConnectionFactory {
    def apply(setter: (ConnectionConfig => Unit)): Either[Throwable, ConnectionFactory] = {
      allCatch either {
        val factory = new MQFactory
        setter(new ConnectionConfig(factory))
        new ConnectionFactory(factory)
      }
    }
  }

コードの詳細はさておき「ConnectionFactory クラスの connect メソッドと、ConnectionFactory オブジェクトの apply メソッドが Either を返している」というのがポイントです。

さて、ここからが本題ですが、これを match case で処理*2しようとすると…

  object Connection {
    def apply(setter: (ConnectionConfig => Unit)): Either[Throwable, Connection] = ConnectionFactory(setter) match {
      case Right(factory) => factory.connect() match {
        case Right(connection) => Right(connection)
        case other => other
      }
      case other => other
    }
  }

こんな感じで match case がネストしまくりな状態に陥ります。
今回のパターンでは、ネストが二段ですが、これが三段・四段と深くなると更に難読化していきます。

そこで、実際に ConnectionFactory を使う所では、下記のようにしています。

  object Connection {
    def apply(setter: (ConnectionConfig => Unit)): Either[Throwable, Connection] = for {
      factory    <- ConnectionFactory(setter).right
      connection <- factory.connect().right
    } yield connection
  }

Scala の for は、Haskell の do 記法のように使えます。

突っ込み・添削 大歓迎。

*1:コードの断片であるため、package や import などが抜けています

*2:この箇所は、デバックしていないので、正しく動作するか不明です