其实问题不大。一个完整的解决方案可能会使用 Reader、IO 和 State monads 以及 Iteratee 和 lens,但这里有一个更简单的版本:
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)
}
}
看,妈,没有 vars!
当然,状态可能会变得非常复杂,但这就是镜头的用武之地。使用镜头,很容易查阅和更改(使用新值复制)任意复杂的数据结构。
对事件使用 iteratee 是很自然的——在非常简单的意义上,“onTrade”变成了一个迭代器,如果每个事件由部分函数组成,则“onTrade”成为一个由枚举器(“生成”事件的东西)调用的迭代器,你可以折叠所有将它们合并为一个偏函数。
或者,State monad 可以与 IO monads 结合起来进行理解。
最后,还有延续的选项。如果某些处理需要接收一系列事件,那么每个事件的结果可以是一个延续,而延续本身成为状态的一部分。