自分自身を引用するには:
では、モナドがあるのに、なぜ Applicative Functor を気にする必要があるのでしょうか? まず第一に、処理したい抽象化のいくつかに対してモナドのインスタンスを提供することは単純に不可能です — <code>Validation は完璧な例です。
2 番目に (そして関連して)、仕事を成し遂げる最も強力でない抽象化を使用することは、堅実な開発プラクティスです。原則として、これにより、他の方法では不可能な最適化が可能になる可能性がありますが、さらに重要なことは、記述したコードがより再利用しやすくなるということです。
最初の段落を少し拡張すると、モナド コードとアプリケーション コードのどちらかを選択できない場合があります。検証をモデル化するために Scalaz (モナド インスタンスを持たない、または持たない)を使用する理由については、その回答の残りの部分を参照してください。Validation
最適化のポイントについて: これが Scala または Scalaz で一般的に関連するようになるまでには、おそらくしばらく時間がかかるでしょうが、たとえばHaskell のドキュメントをData.Binary
参照してください。
Applicative スタイルはbinary
、読み取りをグループ化してコードを最適化しようとするため、コードが高速になる場合があります。
適用可能なコードを書くことで、計算間の依存関係について不必要な主張をすることを避けることができます — 同様のモナドコードがコミットするような主張。十分にスマートなライブラリまたはコンパイラは、原則としてこの事実を利用できます。
このアイデアをもう少し具体的にするために、次のモナド コードを考えてみましょう。
case class Foo(s: Symbol, n: Int)
val maybeFoo = for {
s <- maybeComputeS(whatever)
n <- maybeComputeN(whatever)
} yield Foo(s, n)
-comprehension は、for
多かれ少なかれ次のようなものに脱糖します。
val maybeFoo = maybeComputeS(whatever).flatMap(
s => maybeComputeN(whatever).map(n => Foo(s, n))
)
maybeComputeN(whatever)
に依存しないことはわかっていますs
(これらが、舞台裏で変更可能な状態を変更していない行儀の良いメソッドであると仮定すると) が、コンパイラは依存しません。その観点からは、s
計算を開始する前に知る必要がありますn
。
適用可能なバージョン (Scalaz を使用) は次のようになります。
val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))
ここでは、2 つの計算の間に依存関係がないことを明示的に述べています。
(もちろん、この|@|
構文はかなりひどいものです。いくつかの議論と代替案については、このブログ投稿を参照してください。)
ただし、最後のポイントは本当に最も重要です。問題を解決する最も強力でないツールを選択することは、非常に強力な原則です。getPhoneByUserId
たとえば、メソッド内でモナド構成が本当に必要な場合もありますが、多くの場合、そうではありません。
Haskell と Scala の両方が現在、アプリカティブ ファンクターを使用するよりも (構文的になど) モナドを使用する方がはるかに便利であることは残念ですが、これはほとんど歴史的な偶然の問題であり、イディオム ブラケットのような開発は正しい方向への一歩です。方向。