Not much of a problem, actually. A full solution would probably use Reader, IO and State monads plus Iteratee and lenses, but here's a simpler version:
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)
}
}
Look, ma, no vars!
State can become quite complex, of course, but that's where lenses come in. With lenses, it is pretty easy to consult and change (copy with new value) arbitrarily complex data structures.
Using iteratees is natural for events -- in a very simple sense, "onTrade" becomes an iteratee that gets invoked by an enumerator (the thing that "generates" the events) with each event if composed from partial function, you can fold all of them into a single partial function.
Alternatively, State monads can be combined with IO monads on for-comprehensions.
Finally, there's the option of continuations. If some processing requires a chain of events to be received, then the result of each event can be a continuation, and the continuation themselves become part of the state.