40

I'd like to use val to declare multiple variable like this:

val a = 1, b = 2, c = 3

But for whatever reason, it's a syntax error, so I ended up using either:

val a = 1
val b = 2
val c = 3

or

val a = 1; val b = 2; val c = 3;

I personally find both options overly verbose and kind of ugly.

Is there a better option?

Also, I know Scala is very well thought-out language, so why isn't the val a = 1, b = 2, c = 3 syntax allowed?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Xavi
  • 20,111
  • 14
  • 72
  • 63

5 Answers5

74

The trivial answer is to declare them as tuples:

val (a, b, c) = (1, 2, 3)

What might be interesting here is that this is based on pattern matching. What is actually happens is that you are constructing a tuple, and then, through pattern matching, assigning values to a, b and c.

Let's consider some other pattern matching examples to explore this a bit further:

val DatePattern = """(\d{4})-(\d\d)-(\d\d)""".r
val DatePattern(year, month, day) = "2009-12-30"
val List(rnd1, rnd2, rnd3) = List.fill(3)(scala.util.Random.nextInt(100))
val head :: tail = List.range(1, 10)

object ToInt {
  def unapply(s: String) = try {
    Some(s.toInt)
  } catch {
    case _ => None
  }
}

val DatePattern(ToInt(year), ToInt(month), ToInt(day)) = "2010-01-01"

Just as a side note, the rnd example, in particular, may be written more simply, and without illustrating pattern matching, as shown below.

val rnd1, rnd2, rnd3 = scala.util.Random.nextInt(100)
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 2
    It's worth noting that `val (a, b, c) = (1, 2, 3)` does seem to be the generally-accepted idiom for multiple assignment, preferable to separate assignment statements. At least, that has been my impression reading through community-developed code. – Daniel Spiewak Dec 30 '09 at 22:43
  • 19
    It is also worth noting that you can set multiple vals to the same value with the intuitive "val x, y, z = 42". – Mitch Blevins Dec 31 '09 at 02:25
  • 2
    There is also `val seq@Seq(a, b, c) = Seq(1,2,3)` which not only defines the values `a`, `b` and `c`, but also the seqeunce `seq`! – Mullefa May 27 '15 at 22:37
  • Why scala don't allow such simple things? – Eric Jul 09 '16 at 17:37
  • @EricWang It does allows it, as shown by my first example. If the question is, rather, "why doesn't Scala provide a special syntax to make this easier", then the answer is that one of the goals of Scala is not having special syntax or special cases in the language, but generic mechanisms that follow a well defined pattern and can be used used for multiple things. – Daniel C. Sobral Jul 10 '16 at 21:11
  • @DanielC.Sobral Can't agree with that, from my view, the syntax scala provides is special `val (a, b, c) = (1, 2, 3)`, while what c and Java provide is normal, and easier to read & maintain `int a=0,b,c=1`. – Eric Jul 10 '16 at 21:26
  • @EricWang Where _else_ does Java/C use `int a=0,b,c=1`? In Scala's case, you can do `for ((a, b, c) <- tuples)` and `(1, 2, 3) match { case (a, b c) =>` as well as the `val` case. Furthermore, that's just the deconstructor for tuples. You could do `val Some(a) = someOption`, and, in fact, you can have that for your own type. How do you extend Java/C syntax to allow you to extend `int a=0,b,c=1` in a way you can add your own types? You can't, because `int a=0,b,c=1` is a special case syntax that does only one thing and can't be extended. – Daniel C. Sobral Jul 10 '16 at 23:17
  • any reason why the val names cannot be uppercase? eg: `val (A,B,C) = (1,2,3)` => gives not found value error A,B – Raffaello Dec 07 '19 at 15:50
  • 1
    @Raffaello Because capitalized words are used for pattern matching. That line is comparing the value of `A` to 2, and so forth, and will throw an exception if it doesn't. While that's useless on a `val` assignment, it is a pattern matching rule, and useful in other cases. – Daniel C. Sobral Dec 19 '19 at 17:47
25

Daniel's answer nicely sums up the correct way to do this, as well as why it works. Since he already covered that angle, I'll attempt to answer your broader question (regarding language design)...

Wherever possible, Scala strives to avoid adding language features in favor of handling things through existing mechanisms. For example, Scala doesn't include a break statement. However, it's almost trivial to roll one of your own as a library:

case object BreakException extends RuntimeException

def break = throw BreakException

def breakable(body: =>Unit) = try {
  body
} catch {
  case BreakException => ()
}

This can be used in the following way:

breakable {
  while (true) {
    if (atTheEnd) {
      break
    }

    // do something normally
  }
}

(note: this is included in the standard library for Scala 2.8)

Multiple assignment syntaxes such as are allowed by languages like Ruby (e.g. x = 1, y = 2, z = 3) fall into the category of "redundant syntax". When Scala already has a feature which enables a particular pattern, it avoids adding a new feature just to handle a special case of that pattern. In this case, Scala already has pattern matching (a general feature) which can be used to handle multiple assignment (by using the tuple trick outlined in other answers). There is no need for it to handle that particular special case in a separate way.

On a slightly different aside, it's worth noting that C's (and thus, Java's) multiple assignment syntax is also a special case of another, more general feature. Consider:

int x = y = z = 1;

This exploits the fact that assignment returns the value assigned in C-derivative languages (as well as the fact that assignment is right-associative). This is not the case in Scala. In Scala, assignment returns Unit. While this does have some annoying drawbacks, it is more theoretically valid as it emphasizes the side-effecting nature of assignment directly in its type.

Daniel Spiewak
  • 54,515
  • 14
  • 108
  • 120
  • What if I want to declare 2 variables but do not want to assign them immediately? Like var a: MyClass, b: MyClass? – Phương Nguyễn Jul 25 '10 at 14:16
  • I hope you are still watching this question. `val x = y = z = 1` works in Scala. It seems to be short for `val x = 1; val y = 1; val z = 1;` and not the same as the C version. That is, the expression to the right of `=` is replicated and evaluated multiple times. Is that how it is defined? – RussAbbott Nov 16 '13 at 18:04
  • For example `val x, y, z = (new java.util.Random).nextInt` sets `x`, `y`, and `z` to three different values. – RussAbbott Nov 16 '13 at 18:12
17

I'll add one quirk here, because it hit myself and might help others.

When using pattern matching, s.a. in declaring multiple variables, don't use Capital names for the variables. They are treated as names of classes in pattern matching, and it applies here as well.

  val (A,B)= (10,20)    // won't work
  println(A)

Error message does not really tell what's going on:

src/xxx.scala:6: error: not found: value A
  val (A,B)= (10,20)
       ^ 
src/xxx.scala:6: error: not found: value B
  val (A,B)= (10,20)
         ^ 
src/xxx.scala:7: error: not found: value A
  println(A)
          ^

I thought `-ticking would solve the issue but for some reason does not seem to (not sure, why not):

  val (`A`,`B`)= (10,20)
  println(A)

Still the same errors even with that.

Please comment if you know how to use tuple-initialization pattern with capital variable names.

akauppi
  • 17,018
  • 15
  • 95
  • 120
  • 3
    I detest that 'feature' of identifer naming in Scala. It's OK if you know about it, but it keeps tripping people up (see lots of other SO questions) and is completely baffling until you twig what is going on... It smacks of Fortan's "God is real - unless declared integer" – The Archetypal Paul Oct 10 '12 at 11:06
  • I agree. And with other similar 'sugar' of Scala. It would be good to have a flag to rule out such shortcuts. However... my original issue still remains. Is there any way to use CAPITAL variable names with tuple initialization? – akauppi Oct 10 '12 at 12:43
  • 1
    Just hit this myself. Fortunately googling 'scala tuple assignment val "not found"' led to this answer, avoiding me much bamboozlement. – timday Apr 01 '13 at 22:34
  • `val next = { var n = 0; () => { n = n + 1; n } }; val A, B, C, D, E, F, G, H = next()` works (A=1, b=2, C=3, D=4, E=5, F=6, G=7, H=8). – Dave Swartz Feb 16 '14 at 17:08
  • @Dave-swartz That's clever, but not something I'd start to use on a regular basis for initialization. – akauppi Feb 22 '14 at 19:18
  • Agreed. Only for extremes, but it does work well in some cases. E.g., if you already have an `Iterator` and want to make constants out of its values. `val A, B, C, D, E, F, G, H = iterator.next()` – Dave Swartz Feb 22 '14 at 19:34
  • @DaveSwartz That may well be useful if I do a little wrapper around List that extracts its entries in order. However, there is no check that the number of identifiers and the number of list items would match. – akauppi Mar 17 '15 at 12:43
  • 1
    Soon the syntax is `val (A @ _, B @ _) = (1,2)`. See https://issues.scala-lang.org/browse/SI-8044 where the fix takes the general approach. Now that you mention it, it doesn't make for cool syntax. Also, your intuition that backquotes "invert" the varid syntax hiccup is interesting. – som-snytt Apr 26 '16 at 18:40
9

If all your variables are of the same type and take same initial value, you could do this.

val a, b, c: Int = 0;
smartnut007
  • 6,324
  • 6
  • 45
  • 52
  • 3
    Note that the `0` there is actually a function. Replacing it with something like Math.random provides two different values. – akauppi Jan 28 '16 at 13:10
  • Thank you for mentioning this thing. But what if all my variables are NOT the same type? – akki May 06 '19 at 06:29
8

It seems to work if you declare them in a tuple

scala> val (y, z, e) = (1, 2, 45)
y: Int = 1
z: Int = 2
e: Int = 45
scala> e
res1: Int = 45

Although I would probably go for individual statements. To me this looks clearer:

val y = 1
val z = 2
val e = 45

especially if the variables are meaningfully named.

Joe
  • 46,419
  • 33
  • 155
  • 245