Reputation: 51
I just start my FP journey while learning Scala.
Now a requirement is to maintain a List[String]
in an unfiltered web application. When a POST
request is sent to an endpoint, the List should be updated from a file. And the list will be used when a GET request is sent to the same endpoint.
Now, I am trying to avoid using var
to saving the List. I know sometimes we have to use var but just curious that is there an elegant way to deal with the case. I've tried using scalaz.State Iterator and Steam. But got stuck since I have no idea how to pass the current immutable state to the next request. Any suggestion, please?
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
Then I don't know how can I pass the current state to the next visit as input without using var
.
Upvotes: 1
Views: 433
Reputation: 842
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.
Upvotes: 1