まずDirective0とDirective1の型定義を見て欲しい。
type Directive0 = Directive[Unit]
type Directive1[T] = Directive[Tuple1[T]]
Directive0がDirective1[Unit]であればそこまで問題なかったのだが、このように型パラメータはUnitとTuple1[T]となっている。なぜこのような定義になっているのかは正確には分からないけど、おそらくDSLとしての使い勝手を優先したのだと思う。directive0 { _ => ... } よりは directive0 { } と書けたほうが良い的な。
またDirective1にはimplicit conversionでmapとflatMapが実装されているのでfor式で扱うことができるが、Directive0にはその2つのメソッドは存在しない(代わりにtmapとtflatMapはある)つまり両方を同時にfor式で処理することができないという問題が発生する。
次のようなコードは動かない。
for {
value1 <- directive1A
_ <- directive0A
value2 <- directive1B
} yield (value1, value2)
Directive0はそんなに利用頻度は高くないのだけど、バリデーションや認可など値を返さずに処理を通すか通さないかだけを判断するところでは使うので、これではちょっと困る。Akka HTTP的にはfor式など使わずにひたすらネストさせていくのを推奨しているのかもしれないけど。。。でもfor式も使いたい!
というわけでimplicit conversionでDirective0にmapとflatMapを生やす。ついでにDirective1[Unit]としても振る舞えるように型変換もしておく。
implicit class Directive0ForComprehensionSupport(directive0: Directive0) {
def map[T](f: Unit => T): Directive1[T] = directive0.tmap(f)
def flatMap[T](f: Unit => Directive1[T]): Directive1[T] = directive0.tflatMap(f)
}
implicit def directive0ToDirective1(directive0: Directive0): Directive1[Unit] =
directive0.tflatMap(provide)
ちょっとワークアラウンドっぽいやり方だけどやりたい事は実現できる。ご利用は計画的に👻