Не большая проблема, на самом деле. Полное решение, вероятно, будет использовать 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» становится итератором, который вызывается перечислителем (то есть «генерирует» события) с каждым событием, если оно составлено из частичной функции. сложите их все в одну частичную функцию.
В качестве альтернативы, монады состояний могут быть объединены с монадами ввода-вывода в целях понимания.
Наконец, есть возможность продолжения. Если для некоторой обработки требуется цепочка событий, то результат каждого события может быть продолжением, а сами продолжения становятся частью состояния.