prayagupadhyay
prayagupadhyay

Reputation: 31252

scala functional way to store state

I want to store a state (key -> value) in scala using functional way. I probably learned while ago in Odersky class but can not remember any more.

Here's my non-functional approach;

import org.scalatest.{FunSuite, Matchers}

trait EventHandler

class StatefulNonFn {

  type EventName = String

  private var state = Map.empty[EventName, EventHandler]

  def update(name: String): EventHandler = {
    state.get(name).fold {
      val handler = new EventHandler {}
      state += name -> handler
      handler
    }(eh => eh)
  }

}

class NonFunctionalStateSpec extends FunSuite with Matchers {

  test("stateful") {
    val stateResult = new StatefulNonFn().update("MusicAdded")
    stateResult.isInstanceOf[EventHandler] shouldBe true
  }

}

One attempt I made is to make the state a "function of EventName and previousState" which makes sense but now I can't figure out how do I store those all states?

My first call is going to be fine because the state is empty in that case.

import org.scalatest.{FunSuite, Matchers}

trait EventHandler

class Stateful {

  type EventName = String

  private val stateFn = new ((String, Map[EventName, EventHandler]) => Map[EventName, EventHandler]) {
    override def apply(name: String, prevState: Map[EventName, EventHandler]): Map[EventName, EventHandler] = {
      val handler = new EventHandler {}
      prevState + (name -> handler)
    }
  }

  def initState = Map.empty[EventName, EventHandler]

  def update(name: String, prevState: Map[EventName, EventHandler]) = stateFn(name, prevState)
}

class FunctionalStateSpec extends FunSuite with Matchers {

  test("stateful") {
    val stateHelper = new Stateful()
    val stateResult = stateHelper.update("MusicAdded", stateHelper.initState)

    stateResult.keys.size shouldBe 1

    val stateResult1 = stateHelper.update("MusicDeleted", stateResult)
    stateResult1.keys.size shouldBe 2

    //what i obviously want is something like this without me wanting to store the previousStates

    //stateHelper.update("MusicAdded1")
    //stateHelper.update("MusicAdded2")

  }

}

I am not sure, maybe something eventually has to be mutable. How do I Store the previous states in above case? without the client being the one to supply it in each call. Because state can be updated from 5 separate clients without knowing the previous state.

Upvotes: 3

Views: 744

Answers (1)

caeus
caeus

Reputation: 3716

It just turns out that if you want to do some useful program, you need mutations and you need state. You need to do IO, like saving into the database (which could return some different ID every time you do it), or getting a random number, or the current timestamp, or printing into console, etc.

If you are doing functional programming, is not about doing pure, deterministic, total stuff. It's about isolating sideffects (like mutations and state) away.

So strictly pure languages like Haskell do it by returning actions (or plans, or descriptions of actions...) instead of performing them. The runtime performs those actions, so that way you have two things:

  1. Your pure and sexy program

  2. The runtime, in charge of doing the dirty stuff.

However Scala doesn't just expects you to return actions so that the runtime executes them... You need to do it yourself.

That being said, your solution is perfectly ok, if you don't want to go to dark places. Otherwise, I'd recommend you to read this, an article from John Degoes that basically explains how to do what you want (by simultaneously defining what a freemonad is).

Upvotes: 3

Related Questions