1

I'm doing a course in Coursera about Scala functional programming. I'm in the 6th week.

I have the following code:

/* define the map of numbers to letters */ 
val nmem = Map(
  '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL", 
  '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ"
)

/* invert the map to get a map of letters to digits */
val charCode: Map[Char, Char] = 
  for {
       (digit, str) <- nmem
       ltr <- str
  } yield ltr -> digit

My question is how does the for-comprehension work? nmem introduces the key (char) and the value (string) into digit and str. And later we have: ltr <- str which I don't know how it works because I don't understand how the program knows that ltr is a char instead of an string.

Thank you in advance.

Metropolis
  • 2,018
  • 1
  • 19
  • 36
David Zamora
  • 383
  • 1
  • 4
  • 15
  • thats like a nested for loop in imperative programming. – Ramesh Maharjan Jun 27 '18 at 16:17
  • 1
    When you call `map` method on string, string is treated like sequence of chars. This second `<-` is the same to `map` method call. – Yevhenii Popadiuk Jun 27 '18 at 16:22
  • Answers to [Confused with the for-comprehension to flatMap/Map transformation](https://stackoverflow.com/questions/14598990/confused-with-the-for-comprehension-to-flatmap-map-transformation) might be useful, but doesn't look like a 100% duplicate, if you look at it closely. – Andrey Tyukin Jun 27 '18 at 16:57

3 Answers3

3

This for-comprehension is desugared into

nmem.flatMap { case (digit, str) => str.map { ltr => (ltr, digit) } }

Since nmem has type Map[Char, String], the compiler knows that (digit, str) must be of type (Char, String). Thus, it knows that str is of type String. The elements of a String are of type Char, thus the type of ltr is inferred to be Char.

If you wanted to write down all the gory details of the type inference, you would get something like this:

nmem.flatMap[(Char, Char), Map[Char, Char]]{ 
  case (digit: Char, str: String) => 
  str.map[(Char, Char), Seq[(Char, Char)]]{ 
    (ltr: Char) => (ltr, digit) 
  } 
}

Fortunately, this is not necessary, because all those types can be inferred automatically.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Thank you for your answer. I understand now how it works. Nevertheless if we create this new method: /* define a function that returns the numbers of a given word */ def wordCode(word: String): String = word map charCode wordCode("JAVA") How is possible it works if we are passing a String to a charCode? This code return a String instead a map. – David Zamora Jun 28 '18 at 07:45
  • Are we using now charCode like a map method? – David Zamora Jun 28 '18 at 08:10
  • @zamora14 There are no `String`s passed to `charCode`. Since you invoke `map` on a `String`, the elements of the `String` (that is, characters of type `Char`) are passed to `charCode` one by one. – Andrey Tyukin Jun 28 '18 at 08:28
0

The syntax item <- collection is commonly used to iterate through each item in a collection.

A simple example would be:

for(mapping <- map){
   println(mapping)
}

In your case, this is essentially a nested for loop using yield syntax.

By default it will create a List and accumulate all the items.

Written with a bit more syntax may help:

val charCode : Map[Char,Char] = {  // Cast our result to a Map of (Char, Char)
    for ((digit, str) <- nmem;     // for every key-value pair (Char, String) in nmem
         ltr <- str)               // for every ltr (Char) in str (String)
    yield ltr -> digit             // add a new mapping to the map
}

You can check out https://docs.scala-lang.org/../for-comprehensions.html for more details

-1

The first one actually (digit, str) <- nmem, gets one tuple from Map[String, String], and next one ltr <- str gets chars from that string.

Shankar Shastri
  • 1,134
  • 11
  • 18