ChatGPT解决这个技术问题 Extra ChatGPT

What is the difference between a var and val definition in Scala?

What is the difference between a var and val definition in Scala and why does the language need both? Why would you choose a val over a var and vice versa?

Loved this question as I wondered the same thing.

P
Peter Mortensen

As so many others have said, the object assigned to a val cannot be replaced, and the object assigned to a var can. However, said object can have its internal state modified. For example:

class A(n: Int) {
  var value = n
}

class B(n: Int) {
  val value = new A(n)
}

object Test {
  def main(args: Array[String]) {
    val x = new B(5)
    x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
    x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
    x.value.value = 6 // Works, because A.value can receive a new object.
  }
}

So, even though we can't change the object assigned to x, we could change the state of that object. At the root of it, however, there was a var.

Now, immutability is a good thing for many reasons. First, if an object doesn't change internal state, you don't have to worry if some other part of your code is changing it. For example:

x = new B(0)
f(x)
if (x.value.value == 0)
  println("f didn't do anything to x")
else
  println("f did something to x")

This becomes particularly important with multithreaded systems. In a multithreaded system, the following can happen:

x = new B(1)
f(x)
if (x.value.value == 1) {
  print(x.value.value) // Can be different than 1!
}

If you use val exclusively, and only use immutable data structures (that is, avoid arrays, everything in scala.collection.mutable, etc.), you can rest assured this won't happen. That is, unless there's some code, perhaps even a framework, doing reflection tricks -- reflection can change "immutable" values, unfortunately.

That's one reason, but there is another reason for it. When you use var, you can be tempted into reusing the same var for multiple purposes. This has some problems:

It will be more difficult for people reading the code to know what is the value of a variable in a certain part of the code.

You may forget to re-initialize the variable in some code path, and end up passing wrong values downstream in the code.

Simply put, using val is safer and leads to more readable code.

We can, then, go the other direction. If val is that better, why have var at all? Well, some languages did take that route, but there are situations in which mutability improves performance, a lot.

For example, take an immutable Queue. When you either enqueue or dequeue things in it, you get a new Queue object. How then, would you go about processing all items in it?

I'll go through that with an example. Let's say you have a queue of digits, and you want to compose a number out of them. For example, if I have a queue with 2, 1, 3, in that order, I want to get back the number 213. Let's first solve it with a mutable.Queue:

def toNum(q: scala.collection.mutable.Queue[Int]) = {
  var num = 0
  while (!q.isEmpty) {
    num *= 10
    num += q.dequeue
  }
  num
}

This code is fast and easy to understand. Its main drawback is that the queue that is passed is modified by toNum, so you have to make a copy of it beforehand. That's the kind of object management that immutability makes you free from.

Now, let's covert it to an immutable.Queue:

def toNum(q: scala.collection.immutable.Queue[Int]) = {
  def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
    if (qr.isEmpty)
      num
    else {
      val (digit, newQ) = qr.dequeue
      recurse(newQ, num * 10 + digit)
    }
  }
  recurse(q, 0)
}

Because I can't reuse some variable to keep track of my num, like in the previous example, I need to resort to recursion. In this case, it is a tail-recursion, which has pretty good performance. But that is not always the case: sometimes there is just no good (readable, simple) tail recursion solution.

Note, however, that I can rewrite that code to use an immutable.Queue and a var at the same time! For example:

def toNum(q: scala.collection.immutable.Queue[Int]) = {
  var qr = q
  var num = 0
  while (!qr.isEmpty) {
    val (digit, newQ) = qr.dequeue
    num *= 10
    num += digit
    qr = newQ
  }
  num
}

This code is still efficient, does not require recursion, and you don't need to worry whether you have to make a copy of your queue or not before calling toNum. Naturally, I avoided reusing variables for other purposes, and no code outside this function sees them, so I don't need to worry about their values changing from one line to the next -- except when I explicitly do so.

Scala opted to let the programmer do that, if the programmer deemed it to be the best solution. Other languages have chosen to make such code difficult. The price Scala (and any language with widespread mutability) pays is that the compiler doesn't have as much leeway in optimizing the code as it could otherwise. Java's answer to that is optimizing the code based on the run-time profile. We could go on and on about pros and cons to each side.

Personally, I think Scala strikes the right balance, for now. It is not perfect, by far. I think both Clojure and Haskell have very interesting notions not adopted by Scala, but Scala has its own strengths as well. We'll see what comes up on the future.


A little late, but... Does var qr = q make a copy of q?
@davips It doesn't make a copy of the object referenced by q. It does make a copy -- on the stack, not the heap -- of the reference to that object. As for performance, you'll have to be more clear about what "it" you are speaking of.
Ok, with your help and some info ((x::xs).drop(1) is exactly xs, not a "copy" of xs) from here link I could understand. tnx!
"This code is still efficient" - is it? Since qr is an immutable queue, every time the expression qr.dequeue is called it makes a new Queue (see <github.com/scala/scala/blob/2.13.x/src/library/scala/collection/…).
@Owen Yes, but notice that it is a shallow object. The code is still O(n) whether mutable, if you copy the queue, or immutable.
S
Stefan Kendall

val is final, that is, cannot be set. Think final in java.


But if I understand correctly (not a Scala expert), val variables are immutable, but the objects they reference don't have to be. According to the link Stefan posted: "Here names reference cannot be changed to point to a different Array, but the array itself can be modified. In other words the contents/elements of the array can be modified." So it is like how final works in Java.
Exactly why I posted it as is. I can call += on a mutabled hashmap defined as a val just fine-I believe its exactly how final works in java
Ack, I thought the built-in scala types could do better than simply allowing re-assignment. I need to fact-check.
I was confusing scala's immutable Sequence types with the general notion. Functional programming has me all turned around.
I added and removed a dummy character in your answer so I could give you the upvote.
A
Afzaal Ahmad Zeeshan

In simple terms:

var = variable

val = variable + final


I thought it was more variable and value
S
Stefan Kendall

val means immutable and var means mutable.

Full discussion.


This is just not true. The linked article gives a mutable array and calls it immutable. No serious source.
Not true indeed. Try that val b = Array[Int](1,2,3) b(0) = 4 println(b.mkString(" ")) println(" ")
P
Peter Mortensen

The difference is that a var can be re-assigned to whereas a val cannot. The mutability, or otherwise of whatever is actually assigned, is a side issue:

import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.

Whereas:

val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.

And hence:

val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.

If you are building a data structure and all of its fields are vals, then that data structure is therefore immutable, as its state cannot change.


That is only true if the classes of those fields are immutable as well.
Yeah - I was going to put that in but I thought it might be a step too far! It's also an arguable point I would say; from one perspective (albeit not a functional one) its state does not change even if the state of its state does.
Why is it still so hard to create an immutable object in a JVM language? Furthermore, why didn't Scala make objects immutable by default?
M
Mario Galic

Thinking in terms of C++,

val x: T

is analogous to constant pointer to non-constant data

T* const x;

while

var x: T 

is analogous to non-constant pointer to non-constant data

T* x;

Favoring val over var increases immutability of the codebase which can facilitate its correctness, concurrency and understandability.

To understand the meaning of having a constant pointer to non-constant data consider the following Scala snippet:

val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)

Here the "pointer" val m is constant so we cannot re-assign it to point to something else like so

m = n // error: reassignment to val

however we can indeed change the non-constant data itself that m points to like so

m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)

I think Scala didn't take immutability to its final conclusion: constant pointer and constant data. Scala missed an opportunity to make objects immutable by default. Consequently, Scala doesn't have the same notion of value as does Haskell.
@DerekMahar you're right, but an object can present itself as perfectly immutable, while still using mutability in its implementation, e.g. for performance reasons. How would the compiler be able to separate between true mutability and internal-only mutability?
E
Erwin Smout

"val means immutable and var means mutable."

To paraphrase, "val means value and var means variable".

A distinction that happens to be extremely important in computing (because those two concepts define the very essence of what programming is all about), and that OO has managed to blur almost completely, because in OO, the only axiom is that "everything is an object". And that as a consequence, lots of programmers these days tend not to understand/appreciate/recognize, because they have been brainwashed into "thinking the OO way" exclusively. Often leading to variable/mutable objects being used like everywhere, when value/immutable objects might/would often have been better.


This is the reason that I prefer Haskell over Java, for example.
R
Rollen Holt

val means immutable and var means mutable

you can think val as java programming language final key world or c++ language const key world。


C
Crime_Master_GoGo

Val means its final, cannot be reassigned

Whereas, Var can be reassigned later.


How is this answer different from the 12 answers already submitted?
C
Community

It's as simple as it name.

var means it can vary val means invariable


S
SunTech

Val - values are typed storage constants. Once created its value cant be re-assigned. a new value can be defined with keyword val.

eg. val x: Int = 5

Here type is optional as scala can infer it from the assigned value.

Var - variables are typed storage units which can be assigned values again as long as memory space is reserved.

eg. var x: Int = 5

Data stored in both the storage units are automatically de-allocated by JVM once these are no longer needed.

In scala values are preferred over variables due to stability these brings to the code particularly in concurrent and multithreaded code.


M
Mahesh Chand

Though many have already answered the difference between Val and var. But one point to notice is that val is not exactly like final keyword.

We can change the value of val using recursion but we can never change value of final. Final is more constant than Val.

def factorial(num: Int): Int = {
 if(num == 0) 1
 else factorial(num - 1) * num
}

Method parameters are by default val and at every call value is being changed.


C
Chandan

In terms of javascript , it same as

val -> const var -> var