sdanzig
sdanzig

Reputation: 4500

In Scala, how do I create a new function by setting one argument in another function to the value of a variable?

Here's my failed attempt at doing what I'd like to do:

var currentNum = 0
def adder = (x:Int, y:Int) => x+y
def getNumAdder = {x: Int =>
  val currentNumSnapshot = currentNum
  adder(x, currentNumSnapshot)
}
currentNum = 5
val addFive = getNumAdder
currentNum = 9
val addNine = getNumAdder

println("10 plus 5 is "+addFive(10))
println("10 plus 9 is "+addNine(10))

":paste" that in the Scala REPL and you'll get this:

10 plus 5 is 19
10 plus 9 is 19

I want to be able to call getNumAdder and get a function that adds a snapshot of the current value to an inputted number. What can I change to get the desired behavior?

UPDATE:

As per the warnings promoting referential transparency, I believe this code, a simple example of currying in Scala, does what I need as well.

var currentNum = 0
def adder(x:Int) = (y:Int) => x+y
def getNumAdder(input: Int) = {
  adder(input)
}
currentNum = 5
val addFive = getNumAdder(currentNum)

Upvotes: 0

Views: 94

Answers (3)

Alexey Romanov
Alexey Romanov

Reputation: 170713

def getNumAdder = {
  val currentNumSnapshot = currentNum
  adder((_: Int), currentNumSnapshot) // or (x: Int) => adder(x, currentNumSnapshot)
}

Note that it has to be a def, not a val, so that currentNumSnapshot is evaluated every time getNumAdder is called.

Note that

{x: Int =>
  val currentNumSnapshot = currentNum
  adder(x, currentNumSnapshot)
}

is quite different from

{
  val currentNumSnapshot = currentNum
  (x: Int) => adder(x, currentNumSnapshot)
}

In my version when getNumAdder is evaluated, I first save the current value of currentNum and then return the function which uses this snapshot. In your version the entire block is the function which is returned, so the line val currentNumSnapshot = currentNum is only executed when the function is called (e.g. addFive(10)) and so obviously uses the value of currentNum at that time.

And as goral notes, it's usually a bad idea to do something like that in real code, I am assuming this is only done for understanding.

Upvotes: 2

Martin Ring
Martin Ring

Reputation: 5426

The problem is that you are capturing the value of currentNum when the function is called and not when it is created. Sou you must change it to:

var currentNum = 0
def adder = (x:Int, y:Int) => x + y
def getNumAdder = {
  val currentNumSnapshot = currentNum
  x: Int => adder(x, currentNumSnapshot)
}
currentNum = 5
val addFive = getNumAdder
currentNum = 9
val addNine = getNumAdder

This will work as expected:

scala> println("10 plus 5 is "+addFive(10))
10 plus 5 is 15

scala> println("10 plus 9 is "+addNine(10))
10 plus 9 is 19

Upvotes: 0

goral
goral

Reputation: 1265

I don't know if this one satisfy you but it's working:

  var currentNum = 0                              //> currentNum  : Int = 0
  def adder = (x: Int, y: Int) => x + y           //> adder: => (Int, Int) => Int
  val getNumAdder = { x: Int =>
    val currentNumSnapshot = currentNum
    println(currentNumSnapshot)
    adder(x, currentNumSnapshot)                  //> getNumAdder  : Int => Int = <function1>
  }

  def curriedAdder = adder.curried                //> curriedAdder: => Int => (Int => Int)
  currentNum = 5
  val addFive = curriedAdder(currentNum)          //> addFive  : Int => Int = <function1>
  currentNum = 9
  val addNine = curriedAdder(currentNum)          //> addNine  : Int => Int = <function1>

  println("10 plus 5 is " + addFive(10))          //> 10 plus 5 is 15
  println("10 plus 9 is "+addNine(10))            //> 10 plus 9 is 19

Upvotes: 0

Related Questions