0

Following Scala code gives a compile error stating I cannot assign to a val:

Simplified example:

class State {
  val a = 1
  val b = 2

  def compute( res: =>Int, add : Int ): Unit = {
    res = add + 123456
  }

  compute(a,b)
  compute(b,a)
}

Example closer to my real use:

class Editor {
  var str:String = ""
  var cursor:Int = 0

  case class UndoState(str:String, cursor:Int)

  var undoState = Seq[UndoState]()
  var redoState = Seq[UndoState]()

  def undo(): Unit = if (undoState.nonEmpty) {
    redoState = UndoState(str,cursor) +: redoState
    str = undoState.head.str
    cursor = undoState.head.cursor
    undoState = undoState.tail
  }

  def redo(): Unit = if (redoState.nonEmpty) {
    undoState = UndoState(str,cursor) +: undoState
    str = redoState.head.str
    cursor = redoState.head.cursor
    redoState = redoState.tail
  }
}

As both undo / redo are very similar, I would like to extract the common code into a function, which I wanted to pass source / target pairs as redoState/undoState and the other way around.

Is there some way how to tell where should a function store something? (In C++ I would pass a pointer to member in this situation).

Suma
  • 33,181
  • 16
  • 123
  • 191
  • I am sorry but the question is still not clear to me. INMHO above makes no understanding to me – Jatin Jan 22 '15 at 13:19
  • @Jatin. I have a function which performs some complicated manipulation with two variables. Sometimes I want the variable `a` to be the source and `b` a target, sometimes the other way around. – Suma Jan 22 '15 at 13:24

3 Answers3

1

Use the return value:

def compute( add : Int ): Int = {
  add + 123456
}

val a = compute(b)
val b = compute(a)

pass by reference as you would do it in C++ can't be done in Scala and is generally not what you want to do either. However, you can pass a container that contains a reference to a mutable field:

class Box(var value: Int)

def compute( box: Box, add : Box): Unit = {
  box.value = add.value + 123456
}

val a = new Box(1)
val b = new Box(2)
compute(a, b)
compute(b, a)

Or (a slight variation) make compute member of Box:

class Box(var value: Int) {
  def compute(add: Box): Unit = {
    value = add.value + 123456
  }
}

val a = new Box(1)
val b = new Box(2)
a.compute(b)
b.compute(a)
Suma
  • 33,181
  • 16
  • 123
  • 191
kiritsuku
  • 52,967
  • 18
  • 114
  • 136
  • I understand return value works in my example with simple Int computations, but my real use case is a bit different. both `a` and `b` are used several times. Boxing seems like it should do what I want. – Suma Jan 22 '15 at 13:26
  • It doesn't matter how often your values are used, you can just forward them to as many functions as you want. And function composition is no problem in Scala either if this is what you want. – kiritsuku Jan 22 '15 at 13:28
  • What I did eventually was Boxing and moving the common functionality into function of the boxed type. – Suma Jan 22 '15 at 19:05
1

You can create and pass functions to set the new state (undo or redo):

...
var undoState = Seq[UndoState]()
var redoState = Seq[UndoState]()

def anywaydo(set: (Seq[UndoState]) => Unit) {
  set(...)
  ...
}

def undo {
  anywaydo((state) => undoState = state)
}
thoredge
  • 12,237
  • 1
  • 40
  • 55
  • Sounds nice. I would need to pass a getter as well for the values, as they are both read and written in undo. Having to specify the same name for getter and setter smells to me (I would like to keep the solution as DRY as possible). Still probably nicer than what I have now. – Suma Jan 22 '15 at 14:04
1

You could make your states (mutable) stacks instead of (immutable) seqs, and just pass them into a common function to manipulate:

  def undoredo(states: (Stack[UndoState], Stack[UndoState])): Unit = states match {
      case (Stack(), _) => ()
      case (from, to) => 
          to.push(UndoState(str,cursor))
          val state = from.pop
          str = state.str
          cursor = state.cursor              
  }

  def undo() = undoredo(undoState -> redoState)
  def redo() = undoredo(redoState -> undoState)

Or, if you like scala's fancy "DSL-like" features, you can do this in a fun way with something like:

implicit class StateStack(from: Stack[UndoState]) {
    def ->(to: Stack[UndoState]): Unit = if(from.nonEmpty) {  
        to.push(UndoState(str,cursor)) 
        val state = from.pop
        str = state.str
        cursor = state.cursor            
    }
  }

Then, you can do things like undoState -> redoState for "undo" or redoState -> undoState for "redo" ...

Dima
  • 39,570
  • 6
  • 44
  • 70