Search code examples
scalaround-robin

Scala - Implement round robin without mutable types


I am currently trying to find my way into the world of Scala. Actually I am trying to implement a Round Robin strategy without mutable types.

I have an Scala Object with an initial list of hosts and a method to get the next host.

Object RoundRobin { 
  val optHosts: Option[List[String]] = Option[List[String]]("host1", "host2") // get from Configfile later
  var hosts: List[String] = optHosts.getOrElse(List())

  def url: String = {
    hosts = hosts.tail ++ List(hosts.head)
    hosts(0)
  }

  def execute = ??? // do something with the next host

}

I have read about immutable Queues in Scala but I don't really know how to solve this problem with immutable types. Somehow I would have to remember an index right? Is that one of the cases where it doesn't make sense to use immutable types?


Solution

  • If I understand correctly every time you call execute on the object, it shall use a different element. Then because the object has to encapsulate state, so there's no way around the var

    var hosts = collection.immutable.Queue ++ optHosts.getOrElse(List())
    def url: String = {
       val(element,queue) = hosts.pop
       hosts = queue.enqueue(element)
       element
    }
    

    Regarding your questions...

    Somehow I would have to remember an index right?

    Yes, see above. But also no, see below.

    Is that one of the cases where it doesn't make sense to use immutable types?

    Depends. If you want to keep your object RoundRobin then clearly, that object is mutable, and you so you only have the choice between mutable vals and immutable vars. (well you could also have vars that point to mutable structures, but why would you do that?)

    On the other hand you can also opt for a totally different approach:

    class RoundRobin private(left: List[String], all: List[String]) {
       def this(all :List[String]) = this(all, all)
       assume(all.nonEmpty)
       val theElement = left.headOption.getOrElse(all.head)
       def nextRoundRobin = new RoundRobin(if(left.nonEmpty) left.tail else all, all) 
       def execute = {
           // do something with theElement
           println(theElement)
           // return an object that will execute on the next element
           nextRoundRobin
       }
     }
    
     new RoundRobin(List("1","2")).execute.execute.execute
     // prints 1 2 1
    

    When you use this version of RoundRobin, every time you call execute it will give you a round robin object that will use the next element in round-robin fashion. Clearly the object using RoundRobin can again either be mutable or immutable.