0

With a background from object-oriented programming I am not able to understand how to make immutable lists in Scala.

Example; I want to make a list of 10 random people:

object MyApplication extends App {

  val numberOfPersons = 10 : Int
  val listOfPersons = makeListOfPersons(numberOfPersons) : List[Person]

  def makeListOfPersons( numberOfPersons : Int ) : List[Person] = {
    // TODO: return a immutable list of 10 persons
  }

}

class Person {
  /**
    Generic content, 
    like age and name.
  * */
}

What is the "correct" way of making an immutable list in Scala?

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
Mr.Turtle
  • 2,950
  • 6
  • 28
  • 46

4 Answers4

4

If you know what collection type you want, you may be able to use the tabulate method on that type:

List.tabulate(10)(makePerson)

In this case makePerson is a function that takes an Int and returns the Person object for that Int.

If you don't care about the collection type, you can call map on the range 1 to 10 like this:

(1 to 10).map(makePerson)

If you don't need to use the Int parameter, you can do this:

List.tabulate(10)(_ => makeRandomPerson())
Tim
  • 26,753
  • 2
  • 16
  • 29
1

As the default List in Scala is immutable, the right way to add an element is to return a new list with the new element plus the older elements. As a matter of fact, List has two methods, among others:

+:
++

The first one takes an element, add it as the first element and the rest of the list as it's tail and then returns the resulting list. The other one takes another "collection" as parameter and adds it to the first list at the start. List has another methods for adding the new element as the last one. In Scala, these operations are permitted but take into consideration that always a new instance will be retrieved with the requested modifications as all objects are immutable by default.

As for your code goes, you could try with something like this:

object MyApplication extends App {

 val numberOfPersons: Int = 10
 val listOfPersons: List[Person] = makeListOfPersons(numberOfPersons)

 def makeListOfPersons( numberOfPersons : Int ) : List[Person] = {
   (1 to numberOfPersons).foldLeft(List.empty[Person]){ (accum, elem) =>
     new Person() :: accum
   }
 }

}

(1 to numberOfPersons) creates a range, which could be seen as a List of ints, which will be traversed by foldLeft. This method will iterate through that list, and receives a seed, in this case an empty list of Person. Then, for every element in the int's list, a new Person is created and add to the list, returned as is the last expression and used the accumulator for the next iteration. Finally, a list of ten instances of Person is retrieved.

GuilleK
  • 46
  • 4
1

In this particular case,

List.fill(numberOfPersons){ codeThatCreatesASinglePerson }

seems most appropriate.

In most other cases: Nil creates an empty list, x :: y prepends an element x to list y.

If you want to append to list, instead of prepending to it, then you can take a collection.mutable.ListBuffer, append to it all the elements that you want to have in the list, and then call toList when you're done... or just use the built-in factory methods that do exactly that.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Using `fill` this way does have the disadvantage that it relies on a side-effect and is not functional, so from a purist point of view it would be better to use `tabulate` and seed the random generator from the index so that the code is repeatable and testable. But perhaps I am being pedantic... – Tim Oct 03 '18 at 19:02
  • 1
    @Tim It's not a disadvantage until you specify what your requirements on purity are. You cannot use the index to *seed* the RNG: it would break the distribution (you would always get the same list), and `tabulate` is also not really all that helpful for chaining the RNG state between the calls to `makePerson`. You *could* sample `numberOfPersons` using some monadic RNG, but then you are back to square1: how implement *that*? By the way: both `(1 to 10).map(foo)` and `List.tabulate(10)(_ => foo)` also function only because they have the side-effect of changing the state of the RNG. – Andrey Tyukin Oct 03 '18 at 19:14
  • Functional code is generally considered to be better than non-functional code (at least in the Scala community). But I did say I might be being pedantic :) – Tim Oct 03 '18 at 22:49
  • 1
    @Tim That's a rather strange unconditional statement... The *entire scala community* all of the sudden considers functional code *unconditionally better in all use cases*? Somebody must tell the Dotty-people, they currently have at least 240 `while`-loops in their compiler code... On the less ironic note: no, that cannot work, exactly because it doesn't scale to the "side-effecty at-the-end-of-the-world"-scenarios - someone will have to do that, and it's not going to be purely functional, because in the end of the day, there are these weird physical machines that mutate their state. – Andrey Tyukin Oct 03 '18 at 23:22
  • How did you get from "generally considered to be better [] in the Scala community" to "unconditionally better in all use cases [for] the entire Scala community"? – Tim Oct 04 '18 at 07:32
  • @Tim From the fact that you had to mention it under an answer to a question that didn't ask anything specifically about fp or side effects - it evoked the impression that the statement is somehow universally valid. Even if one doesn't interpret "all use cases" and "entire community" into your statement, it still sounds a bit strange, because it's like claiming that some people cannot tell the difference between a bucket-wheel excavator and a surgical scalpel, yet strongly believe that excavators are "better" (better for what?). – Andrey Tyukin Oct 04 '18 at 11:33
1

There are 5 ways to create List in scala:

Lisp style:

val list = 1::2::3::Nil

this style can also be thought of as a Haskell or functional programming (FP) style.

Java Style:

val list = List(1,2,3)

Scala List with range method

List.range(1, 10)

Create scala List with fill

List.fill(3)(5)

Scala List with tabulate

List.tabulate(5)(n => n * n)

element of the list are created according to the function we supply.

for more info please read this :

Preferred way to create a Scala list

Raman Mishra
  • 2,635
  • 2
  • 15
  • 32