views:

212

answers:

4

Hello,

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("[email protected]")))
  val fredrik = Person("Fredrik", 33, List(EmailAddress("[email protected]"), EmailAddress("[email protected]")))
  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)

+3  A: 

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

Aaron Novstrup
+5  A: 

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
Thank you, that worked too and I didn't have to use a mutable map
Mansoor Ashraf
+2  A: 

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
Thank You, I changed the map to a mutable map and it worked. Not sure if that is the right solution though
Mansoor Ashraf
No need to use a mutable map. You should change `+=` to `+` instead (as already suggested by @Ken Bloom).
missingfaktor
A: 

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("[email protected]") ::
    Nil
  )

  val fredrik = Person(
    "Fredrik",
    33,
    EmailAddress("[email protected]") ::
    EmailAddress("[email protected]") ::
    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
Defining an implicit conversion from `String` to `EmailAddress` seems like an overkill. :)
missingfaktor
True, but the code was *sooooo* boilerplatey, and wide, for trying to fit into the StackOverflow window
Kevin Wright
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
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