8

I am doing a training exercise in Scala and getting this val reassignment error. I don't see where I am reassigning a new value to a val

class personTest
{
  val alf = Person("Alf", 30, List(EmailAddress("alf.kristian@gmail.com")))
  val fredrik = Person("Fredrik", 33, List(EmailAddress("fredrik@vraalsen.no"), EmailAddress("fvr@knowit.no")))
  val johannes = Person("Johannes", 0, Nil)

  val persons = List(alf, fredrik, johannes)

   @Test
  def testNameToEmailAddress
  {
    // Create a map from each persons name to their e-mail addresses,
    // filtering out persons without e-mail addresses
    // Hint: First filter list, then use foldLeft to accumulate...
    val emptyMap: Map[String, List[EmailAddress]] = Map()

    val nameToEmail = persons.filter(_.emailAddresses.length>0).foldLeft(emptyMap)((b,p)=> b+=p.name->p.emailAddresses)

    assertEquals(Map(alf.name -> alf.emailAddresses, fredrik.name -> fredrik.emailAddresses), nameToEmail)
  }

}

and I am getting this error

error: reassignment to val
val nameToEmail = persons.filter(_.emailAddresses.length>0).foldLeft(emptyMap)((b,p)=> b+=p.name->p.emailAddresses)
gturri
  • 13,807
  • 9
  • 40
  • 57
Mansur Ashraf
  • 1,337
  • 3
  • 9
  • 12

4 Answers4

9

b which is the name of a parameter to your closure is itself a val, which cannot be reassigned.

foldLeft works by taking passing the return value of one invocation of the closure as the parameter b to the next, so all you need to do is return b + (p.name->p.emailAddresses). (Don't forget the parentheses for precedence.)

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
3

You're reassigning val b in the expression b+=p.name->p.emailAddresses.

Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
3

Immutable Map does not have a += method. In such case, compiler translates b += p.name -> p.emailAddresses to b = b + p.name->p.emailAddresses. There you have it, reassignment!

missingfaktor
  • 90,905
  • 62
  • 285
  • 365
0

As previously mentioned, the error message is originating in the expression ...b+=bp.name...

But really, you don't need to be doing a foldLeft here at all, a simple mapping should be enough. Any Seq[K->V] can then be converted to a Map[K,V] via the toMap method.

Something like this:

disclaimer: not tested for typos, etc.

class personTest {
  val alf = Person(
    "Alf",
    30,
    EmailAddress("alf.kristian@gmail.com") ::
    Nil
  )

  val fredrik = Person(
    "Fredrik",
    33,
    EmailAddress("fredrik@vraalsen.no") ::
    EmailAddress("fvr@knowit.no") ::
    Nil)

  val johannes = Person(
    "Johannes",
    0,
    Nil)

  val persons = List(alf, fredrik, johannes)

  @Test
  def testNameToEmailAddress {

    val nameToEmailMap =
      persons.view filter (!_.emailAddresses.isEmpty) map {
        p => p.name -> p.emailAddresses
      } toMap

    assertEquals(
      Map(
        alf.name -> alf.emailAddresses,
        fredrik.name -> fredrik.emailAddresses
      ),
      nameToEmailMap
    )
  }
}
Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • 1
    Defining an implicit conversion from `String` to `EmailAddress` seems like an overkill. :) – missingfaktor Oct 11 '10 at 19:05
  • True, but the code was *sooooo* boilerplatey, and wide, for trying to fit into the StackOverflow window – Kevin Wright Oct 11 '10 at 19:33
  • I like the DPP quote (from his book): "I think of implicits like I think of vampires. They are very powerful and very dangerous, and I only invite them into my program's scope when there is a very good reason." – olle kullberg Oct 11 '10 at 19:46
  • 1
    Used in a properly constrained scope I think implicits are perfectly safe, especially implicit conversions from types used in lieu of dynamic typing. An implicit conversion here is basically stating that "I can supply a String whenever an EmailAddress is expected", which seems like a reasonable kind of statement. You drop the boilerplate of all those constructors, but still wrangle things into type-safety ASAP. – Kevin Wright Oct 11 '10 at 22:00