4

I wrote the following simple example to understand how the map method works:

object Main{
  def main (args : Array[String]) = {
    val test = "abc"
    val t = Vector(97, 98, 99)
    println(test.map(c => (c + 1)))               //1 Vector(98, 99, 100)
    println(test.map(c => (c + 1).toChar))        //2 bcd
    println(t.map(i => (i + 1)))                  //3 Vector(98, 99, 100)
    println(t.map(i => (i + 1).toChar))           //4 Vector(b, c, d)
  };
}

I didn't quite understand why bcd is printed at //2. Since every String is treated by Scala as being a Seq I thought that test.map(c => (c + 1).toChar) should have produced another Seq. As //1 suggests Vector(b, c, d). But as you can see, it didn't. Why? How does it actually work?

St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • Although it's written in Haskell, it's how I learned FP in Scala - http://learnyouahaskell.com/making-our-own-types-and-typeclasses#the-functor-typeclass. I would recommend buying and reading https://www.manning.com/books/functional-programming-in-scala. Or, I'd suggest, http://learnyouahaskell.com, which is free online. – Kevin Meredith Jun 23 '16 at 15:06
  • 8
    @KevinMeredith are you seriously assuming one needs to learn Haskell to understand how Scala string works? – Haspemulator Jun 23 '16 at 15:07
  • I posted as a comment, not an answer, for this reason. To understand `map`, my opinion and experience is that it's worthwhile to understand the basics of Functional Programming first - since `map` is a fundamental of FP. – Kevin Meredith Jun 23 '16 at 15:09
  • 4
    @KevinMeredith ... in particular to explain Scala's `CanBuildFrom` which simply doesn't exist in Haskell? – Victor Moroz Jun 23 '16 at 15:10
  • Not an easy reading, but try http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html – Victor Moroz Jun 23 '16 at 15:14
  • 1
    @VictorMoroz - I found [Programming in Scala, 3rd edition](http://www.artima.com/shop/programming_in_scala_3ed)'s chapter 25 (The Architecture of Scala Collections) to give a good explanation of `CanBuildFrom`. Also - good point, Victor, on calling me out about `CanBuildFrom`. I should've read the question rather than assume it was about understanding `map` in FP. – Kevin Meredith Jun 23 '16 at 16:01

2 Answers2

7

This is a feature of Scala collections (String in this case is treated as a collection of characters). The real explanation is quite complex, and involves understanding of typeclasses (I guess, this is why Haskell was mentioned in the comment), but the simple explanation is, well, not quite hard.

The point is, Scala collections library authors tried very hard to avoid code duplication. For example, the map function on String is actually defined here: scala.collection.TraversableLike#map. On the other hand, a naive approach to such task would make map return TraversableLike, not the original type the map was called on (it was the String). That's why they've came up with an approach that allows to avoid both code duplication and unnecessary type casting or too general return type.

Basically, Scala collections methods like map produce the type that is as close to the type it was called at as possible. This is achieved using a typeclass called CanBuildFrom. The full signature of the map looks as follows:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

There is a lot of explanations what is a typeclass and CanBuildFrom around. I'd suggest looking here first: http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html#factoring-out-common-operations. Another good explanation is here: Scala 2.8 CanBuildFrom

Community
  • 1
  • 1
Haspemulator
  • 11,050
  • 9
  • 49
  • 76
  • The example of that is, even if the test was declared as a Seq[Char] it is still converted to a String – Daniel Jun 23 '16 at 15:24
  • 2
    +1 The really short answer is that Scala's collection operations are type-preserving like Smalltalk's, not most-general-type-always like Java's, .NET's (`IEnumerable`) or Ruby's (`Array`). – Jörg W Mittag Jun 23 '16 at 21:59
0

When you use map, this is what is happening : [List|Seq|etc].map([eachElement] => [do something])

map applies some operation to each element of the variable on the left hand-side : "abc".map(letter => letter + 1) will add 1 to each element of the String "abc". And each element of the String abc is called here "letter" (which is of type Char)

"abc" is a String, and as in C++, it is treated as an array of characters. But since test is of type String, the map function gives a String as well.

I tried the following :

val test2 : Seq[Char] = "abc"

but I still get a result of type String, I guess Scala does the conversion automatically from a Seq[Char] to a String

I hope it helped!

Daniel
  • 1,172
  • 14
  • 31