実際、それほど問題はありません。完全なソリューションでは、おそらくReader、IO、Stateモナドに加えて、Iterateeとレンズを使用しますが、より単純なバージョンを次に示します。
case class State(dailyHigh: Int = 0)
object Main {
type Event = (State => State)
def mainLoop(currState: State, events: Stream[Event]): State =
if (events.nonEmpty) {
val newState = events.head(currState)
mainLoop(newState, events.tail)
} else currState
def onTrade(price: Int): Event = (s: State) =>
if (price > s.dailyHigh) s.copy(dailyHigh = price) else s
def main(args: Array[String]) {
val events = onTrade(5) #:: onTrade(2) #:: onTrade(10) #:: onTrade(5) #:: Stream.empty
val finalState = mainLoop(State(), events)
println(finalState)
}
}
ほら、まあ、変数はありません!
もちろん、状態は非常に複雑になる可能性がありますが、レンズの出番です。レンズを使用すると、任意の複雑なデータ構造を調べて変更(新しい値でコピー)するのは非常に簡単です。
反復子の使用はイベントにとって自然です。非常に単純な意味で、「onTrade」は、部分関数で構成されている場合、各イベントで列挙子(イベントを「生成」するもの)によって呼び出される反復子になります。すべてを折りたたむことができます。それらを単一の部分関数にまとめます。
または、状態モナドを内包表記のIOモナドと組み合わせることができます。
最後に、継続のオプションがあります。一部の処理で一連のイベントを受信する必要がある場合、各イベントの結果は継続になる可能性があり、継続自体が状態の一部になります。