上記の関数を書き直すための推奨される方法は、 toxicafunkによって提案されているように、適切なScheduleを使用することです。
def getNameFromUserSchedule(askForName: UIO[String]): UIO[String] =
askForName.repeat(Schedule.doWhile(_.isEmpty))
これは簡潔で読みやすく、一定量の ZIO スタック フレームのみを消費します。
ただし、 Scheduleを使用して作成する必要はありません。
def getNameFromUser(askForName: UIO[String]): UIO[String] =
for {
resp <- askForName
name <- if (resp.isEmpty) getNameFromUser(askForName) else ZIO.succeed(resp)
} yield name
一定量の ZIO スタック フレームを消費します。次のようにすることもできます。
def getNameFromUser(askForName: UIO[String]): UIO[String] =
askForName.flatMap { resp =>
if (resp.isEmpty) getNameFromUser(askForName) else ZIO.succeed(resp)
}
この関数は、脱糖された形ではオリジナルとほぼ同じように見えます。
def getNameFromUser(askForName: UIO[String]): UIO[String] =
askForName.flatMap { resp =>
if (resp.isEmpty) getNameFromUser(askForName) else ZIO.succeed(resp)
}.map(identity)
唯一の違いは finalmap(identity)
です。この関数から生成された ZIO 値を解釈する場合、インタープリターは をidentity
スタックにプッシュし、 を計算してflatMap
から、 を適用する必要がありidentity
ます。ただし、 を計算するためflatMap
に、同じ手順が繰り返される可能性があり、インタープリターidentities
はループ反復と同じ数をスタックにプッシュする必要があります。これはちょっと厄介ですが、インタプリタは、スタックにプッシュする関数が実際には ID であることを知ることができません。better-monadic-forコンパイラ プラグインfor
を使用することで、nice 構文を削除せずにそれらを削除できます。map(identity)
がなければ、map(identity)
インタプリタは を実行しaskForName
、クロージャを使用します。
resp =>
if (resp.isEmpty) getNameFromUser(askForName) else ZIO.succeed(resp)
解釈のために次の ZIO 値を取得します。この手順は任意の回数繰り返される可能性がありますが、インタープリター スタックのサイズは変更されません。
要約すると、ZIO インタープリターがいつ内部スタックを使用するかについて簡単に説明します。
flatMaps
のように連鎖した を計算するときio0.flatMap(f1).flatMap(f2).flatMap(f3)
。このような式を評価するために、インタプリタはf3
スタックにプッシュし、 を調べますio0.flatMap(f1).flatMap(f2)
。f2
次に、スタックに置き、 を調べますio0.flatMap(f1)
。最後f1
に、スタックに置かれ、io0
評価されます (ここで近道を取る可能性のあるインタープリターには最適化がありますが、それは議論には関係ありません)。io0
toの評価後、r0
がf1
スタックからポップされ、 の結果に適用されr0
、新しい ZIO 値 が得られio1 = f1(r0)
ます。io1
が評価されr1
、f2
スタックからポップされて、次の ZIO 値が取得されますio2 = f2(r1)
。最後に、io2
に評価されますr2
。f3
取得するためにスタックからポップされ、式の最終結果である にio3 = f3(r2)
解釈io3
されます。r3
したがって、 を連鎖させることで機能するアルゴリズムがある場合flatMaps
、ZIO スタックの最大深度は、少なくとも の連鎖の長さであると予想する必要がありますflatMaps
。
- のような連鎖した折り畳みを計算する場合
io.foldM(h1, f1).foldM(h2, f2).foldM(h3, f3)
、または連鎖した折り畳みと連鎖した の混合物を計算する場合flatMaps
。エラーがない場合、フォールドは のようflatMaps
に動作するため、ZIO スタックに関する分析は非常に似ています。ZIO スタックの最大深度は、少なくともチェーンの長さであると予想する必要があります。
flatMap
上記のルールを適用するときは、 andの上に直接的または間接的に実装される多くのコンビネータがあることに注意してfoldCauseM
ください。
map
、as
、zip
、zipWith
、<*
、*>
が実装されていfoldLeft
ますforeach
flatMap
fold
、foldM
、catchSome
、catchAll
はmapError
上に実装されていますfoldCauseM
最後になりましたが、ZIO の内部スタックのサイズについてあまり心配する必要はありません。
- 中程度または一定のサイズの入力データに対してのみ反復回数が任意に大きくなる可能性があるアルゴリズムを実装しています
- メモリに収まらない非常に大きなデータ構造をトラバースしている
- ユーザーは、ほとんど労力をかけずにスタックの深さに直接影響を与えることができます (つまり、ネットワーク経由で大量のデータを送信する必要はありません)。