处理scala中状态的不可变方法

时间:2016-08-30 14:42:03

标签: functional-programming state scalability scalaz unfiltered

我在学习Scala的同时开始我的FP之旅。

现在要求在未经过滤的Web应用程序中维护List[String]。将POST请求发送到端点时,应从文件更新列表。当GET请求发送到同一端点时,将使用该列表。

现在,我试图避免使用var来保存列表。我知道有时我们必须使用var,但只是好奇,这是一种处理案例的优雅方式。我尝试过使用scalaz.State Iterator和Steam。但是因为我不知道如何将当前的不可变状态传递给下一个请求而陷入困境。有什么建议吗?

def update = State( l => {
  retrieve(filepath) match {
    case Success(lines) => (lines.split("[,\n\r]").toVector.map (_.trim), true)
    case Failure(_) => {
      log.error(s"Cannot retrieve the file.")
      (l, false)
    }
  }
})

def isContained(message: String) = State(l => (l, l.exists(message.contains)))

/* assume the following get or post method will be invoked when GET or POST request is sent to the endpoint */

def post() = update(Vector.empty) // how can I pass the updated state to the get method

def get(msg: String): Boolean = isContained(msg)(???)._2

然后我不知道如何在不使用var的情况下将当前状态作为输入传递给下次访问。

1 个答案:

答案 0 :(得分:0)

There is no free lunch. If you want to avoid mutability and avoid storing the state somewhere, you need to work with returned values.

State is no more than a function A => B (I simplified it a bit for a purpose), where A is initial state and B is the endresult

So in your case, the model would be looking like:

def post(newMessage: String, state: List[String]): List[String] = {
  newMessage :: state
}

def get(msg: String, state: List[String]): Boolean = {
  state.contains(msg)
}

As you can see here, you need to provide the current state to every post and get. Posts would just the add a new message from the file (put your business logic here), and return a new state. And for get, you need to provide current state, to be able to retrieve something you want. You can rewrite it this way:

def post(newMessage: String): List[String] ⇒ List[String] = state ⇒ {
  newMessage :: state
}

def get(msg: String): List[String] ⇒ Boolean = 
  _.contains(msg)

Notice that post returns you exactly A ⇒ A (where A = List[String]).

ScalaZ State gives you Monad for chaining the functions inside for comprehension as well as some additional conveniences (like map, gets, put etc..) . But essentially - the underlying model would be the same.

This code represents more precisely what State does:

  type YourListState = List[String] ⇒ List[String]

  def post(newMessage: String, state: YourListState): YourListState = li ⇒ {
    newMessage :: state(li)
  }

  def get(msg: String, state: YourListState): List[String] ⇒ Boolean = {
    state(_).contains(msg)
  }

This allows you to combine states and to provide initial value and "run" it whenever you want, not sure that you really need this.