DEGREE PROJECT IN COMPUTER SCIENCE AND ENGINEERING, SECOND CYCLE, 30 CREDITS STOCKHOLM, SWEDEN 2017

Immutability: An Empirical Study in Scala

LUDVIG AXELSSON

KTH ROYAL INSTITUTE OF TECHNOLOGY SCHOOL OF COMPUTER SCIENCE AND COMMUNICATION Immutability: An Empirical Study in Scala

LUDVIG AXELSSON

Master in Computer Science Date: 2017 Supervisor: Philipp Haller Examiner: Mads Dam Swedish title: Oföränderlighet: en empirisk studie i Scala School of Computer Science and Communication i

Abstract

Utilizing immutability is considered to have many desired benefits when it comes to software develop- ment and reasoning about programs. It is also one of the core principles of , and many programming languages have support for specifying immutability. Developers can by spec- ifying immutability write code that, for example, prevent program state from being unintentionally mutated. The Scala programming language is a functional and object-oriented language where devel- opers can specify immutability with reassignable and non-reassignable variables. The in Scala has no built-in support for developers to express the fact that a type is immutable, immutability is instead by convention and considered best practice. However, knowledge about the immutability usage and how prevalent it is in real-world Scala code are until this point non-existent. This project presents an immutability analysis and evaluation of six small-to-large open source projects written in Scala providing empirical data on immutability usage. The analysis investigates the immutability property of templates, where a template refers to one of Scala’s different class types, on three distinct properties: shallow, conditionally deep and deep immutability, where deep is the strongest immutability property. The analysis works as a plug-in for the Scala compiler that statically analyzes the source code of projects. We report immutability statistics for each evaluated project, including three widely used projects, Scala’s standard library, Akka’s actor framework and ScalaTest. Expla- nations to why stronger immutability properties do not hold are also provided. The analysis show that the majority of templates for each project satisfied an immutability property and were not classified as mutable. Because each analyzed project had templates that were assumed to be mutable, as they were unreachable by our analysis, there could potentially be more templates that satisfy an immutability property. Inheritance is shown to be an important factor when it comes to a template’s immutability and mutability was found to be lower for the template types case class and sin- gleton object. This can be seen as intended by the designers of Scala, indicating that these type of class abstractions help programmers utilize immutability. Our results show that immutability is frequently used in Scala and the high degree of immutability usage could be due to the functional nature of the language.

Keywords immutability, empirical study, static analysis ii

Sammanfattning

Att använda immuterbar (oföränderlig) data anses ha många önskvärda fördelar när det kommer till ut- veckling av program och att kunna resonera om dess funktionalitet. Immuterbar data är också en viktig princip inom funktionell programmering och många språk har idag stöd för att ange immuterbarhet. Utvecklare kan i kod ange ifall data ska vara immuterbar för att till exempel förhindra ett programtill- stånd från att oavsiktligt förändras. Programmeringsspråket Scala är ett funktionellt och objektoriente- rat språk där utvecklare kan ange immuterbarhet med två typer av variabler, en som är tilldelningsbar och en som är icke-tilldelningsbar. Typsystemet i Scala har inget inbyggt stöd för utvecklare att uttryc- ka det faktum att en typ är immuterbar, att använda immuterbarhet är i stället konvention och anses vara den bästa metoden. Men uppgifter om hur immuterbarhet egentligen används i riktiga Scala pro- jekt har fram tills nu inte varit tillgängligt. Detta projekt presenterar en immuterbarhetsanalys och en utvärdering av sex små till stora projekt med öppen källkod skrivna i programmeringsspråket Scala. Analysen undersöker immuterbarhetse- genskaper hos Scalas olika typer av klasser med avseende på tre olika egenskaper: ytlig, villkorligt djup och djup immuterbar, där djup är den starkaste immuterbarhetsegenskapen. Analysen fungerar som ett tillägg för Scalas kompilator och utfärdar en statisk analys av källkoden för ett projekt. Statistik om immuterbarhet för varje projekt redovisas och utvärderas, bland annat tre välkända och populära kod- baser, Scalas standard bibliotek, Akka’s actor ramverk och ScalaTest. Förklaringar till varför klasser inte uppfyller en immuterbarhetsegenskap visas också. Analysen visar att majoriteten av alla klasser i projekten har en immuterbarhetsegenskap och var inte klassificerade som muterbara. Eftersom varje projekt hade klasser som antogs vara muterbara för att dessa inte var nåbara för våran analys så kan det potentiellt finnas fler klasser som har en immuter- barhetsegenskap. Vad en klass ärver visar sig vara en viktig faktor när det kommer till vilken typ av immuterbarhetsegenskap den har. Muterbarhet visade sig vara lägre för klasser som är av typen case class and singleton object vilket kan anses vara avsett av Scalas skapare, då dessa klass abstraktioner hjälper programmerare att använda immuterbarhet. Resultaten visar att immuterbarhet används flitigt i Scala och den höga användningsgraden kan vara på grund av att det är ett funktionellt språk.

Keywords immuterbarhet, empirisk studie, statisk analys Contents

1 Introduction 1 1.1 Motivation ...... 1 1.2 Problem Statement ...... 3 1.3 Contribution ...... 3 1.4 Choice of Methodology ...... 3 1.5 Delimitations ...... 4 1.6 Societal, Sustainability and Ethical Aspects ...... 4

2 Background 5 2.1 The Scala Programming Language ...... 5 2.1.1 Classes and Objects ...... 6 2.1.2 Templates (class, object and trait) ...... 6 2.1.3 Types ...... 9 2.2 Functional Programming ...... 10 2.3 Immutability ...... 11 2.3.1 Why Immutability Matters ...... 11 2.3.2 Varieties of Immutability ...... 12 2.3.3 Immutability in Scala ...... 13 2.4 Immutability Analysis ...... 14 2.4.1 Static Program Analysis ...... 14 2.4.2 Scala Compiler Plug-in ...... 15

3 Related Work 17 3.1 Immutability Support in Programming Languages ...... 17 3.2 Immutability with Type Systems ...... 18 3.3 Immutability Analysis ...... 20 3.3.1 Static Code Analysis ...... 20 3.4 An Empirical Study on ++ const and Immutability ...... 21 3.5 Tools for Immutability ...... 22 3.6 Summary ...... 22

4 Methodology 23 4.1 The Immutability Analysis ...... 23 4.1.1 Immutability Properties ...... 24 4.1.2 Detecting Immutability ...... 25 4.1.3 Limitations ...... 27 4.1.4 Immutability Assumptions ...... 28

iii iv CONTENTS

4.2 Evaluation ...... 28

5 Results 29 5.1 Projects Analyzed ...... 29 5.2 Template Statistics ...... 30 5.3 Immutability Statistics ...... 32 5.4 Immutability Attributes ...... 36 5.5 Conditional Deep Immutability ...... 39

6 Discussion 41 6.1 The Empirical Study ...... 41 6.2 Future Work ...... 42 6.3 Conclusion ...... 43

Glossary 44

Bibliography 45

A Source Code and Data 48 A.1 The Analysis Program ...... 48 A.2 Immutability Assumptions ...... 48 A.3 Immutability Attributes ...... 50 Chapter 1

Introduction

This chapter serves as an introduction to the degree project. We begin by describing the motivation and background of the project, followed by the problem statement and overall goal of it. The choice of methodology, delimitations, and contributions of the project are then also explained.

1.1 Motivation

Unintended mutations of a program’s state are one reason for inconsistent behavior and bugs of the program. These mutations might have been introduced by side-effects of functions that developers were unaware of during the program’s implementation. Some programming languages do, for exam- ple, allow arguments of a function to be mutated. The fact that a third-party function can mutate the state of its argument can go undetected. The problem of rogue and complicated state mutations can become difficult to handle when states are shared among objects. One way to avoid undesired muta- tions is to use immutable data instead of mutable data. Immutable data cannot be mutated once created and instead of mutating shared data in memory, data would have to be re-created to include the modi- fications needed. Programmers can then safely share data without the possibility of having it mutate to something else, which is crucial to avoid, for example, race conditions in concurrent programs. Immutability is a property of data types and immutable data is one of the core principles of func- tional programming. In functional programming, programmers are supposed to use functions without side-effects, and a function has no side-effect when it does not mutate any state. Side-effects of func- tions disappear when the data used cannot be mutated. The concept of immutability is important and utilizing immutable data is considered to ease software development and reasoning about programs in numerous ways, for example:

Predictability It is harder to understand and reason about programs that have shared mutable states with unclear interactions. Tracking mutations and maintaining the correct state of a program can be difficult. Using immutable data naturally, avoids state changes and forces the programmer to let data flow and be utilized in a different way throughout the program, making the state of the program more predictable. For example, calling the same function twice would yield the same re- sult, and the outcome is predictable. Without the immutability guarantee, the second call could yield another result because of an underlying state mutation making it less predictable.

Testability Because immutable data can only be changed once during construction, they are inher- ently simpler and easier to unit test. One may reason that by restricting the number of possible mu- tations in a program; the number of potential errors of the program is also reduced. Testing is es- sentially to validate that mutations in the program occur correctly and thus having more mutations

1 2 CHAPTER 1. INTRODUCTION

would require more testing. By restricting the number of mutations in a program, the program has fewer reasons for errors to occur and there are fewer state transitions to test.

Concurrency Immutable data are thread-safe, as data cannot mutate, there is no danger in having multiple threads access the same data at the same time and have synchronization issues.

Modularity Without depending on a local or global state, immutable types and data may be reused in different contexts more easily.

This degree project aims to investigate immutability usage with the Scala programming language as the target. The Scala programming language is a popular functional and object-oriented program- ming language with a strong static type system that was released publicly in 2004 [1]. The language is designed to be high-level with a concise syntax that allows programmers to do complex tasks with less amount of code that is still easy to understand. Scala does not enforce developers to write code in a functional style, and code can be written in both an imperative and functional style. The philosophy is to write code in the style that fits the program’s task [1], the functional style utilizing immutable data is highly encouraged and considered best practice when using Scala. The mix of object-oriented and functional programming, together with other properties of Scala, make this investigation particularly interesting from the immutability perspective because functional programming would by definition use more immutable data. In Scala, developers specify immutability with reassignable and non-reassignable variables on tem- plates. A template1 refers to either a class, case class, trait, object or case object definition, where the definitions beside class are special types of classes. There is no built-in way to know if a template is immutable and it is not possible to express the fact that a type is immutable in Scala. Developers can define types that are in fact immutable but the Scala’s type system does not support any functional- ity to reason about it. This is also true for many mainstream programming language [3] and external tools or manual inspection has to be used to detect immutability. Immutability in Scala is instead by convention and considered best practice. The Scala standard library, for example, systematically distin- guishes between mutable and immutable types by design. For example, the collection package contain two packages mutable and immutable providing the same functionality but with different immutability. All templates that are mutable by design are in the mutable package and immutable classes in the im- mutable package. This allow developers to chose collection depending on if it should be immutable or not. Detecting immutability is a challenging task, and there are no prior data on immutability usage for real-world Scala code. Such data could motivate a feature of the Scala compiler to enforce cer- tain immutability properties, for example, letting programmers explicitly specify if a template is im- mutable. As Coblenz et al. [4] points out, there are very little empirical data about the immutability of real-world applications in general. Empirical data can be used to understand immutability better and as grounds for programming language design decisions.

1Following the terminology of the Scala language specification. [2] CHAPTER 1. INTRODUCTION 3

1.2 Problem Statement

The goal of this degree project is to investigate and attempt to explore the immutability usage of real- world code written in the Scala programming language. To explore immutability usage, an immutabil- ity analysis will be defined and developed to gather immutability statistics. To implement the im- mutability analysis this project will also include research about immutability and methods of introduc- ing immutability. More specifically, the following Research Questions (RQs) is what this project will aim to answer:

RQ1 How can a simple immutability analysis be defined and implemented in Scala? What are the needed immutability properties?

RQ2 Using an implementation of the defined immutability analysis, how frequent is each im- mutability property for the different templates?

RQ3 What are the reasons to why stronger immutability properties are not satisfied?

RQ4 How often are types that are mutable or have a weaker immutability property used on tem- plates whose immutability is conditional? This involves, for example, using a mutable type on an immutable collection.

1.3 Contribution

The statistics, classification of immutability and found observations contribute to the understanding of the Scala language immutability usage and features. More specifically, the contribution includes:

• We present the design and implementation of a novel immutability analysis for Scala.

• An empirical study on the use of immutability in real-world, open-source Scala code.

• Based on the results of the empirical study, a set of explanations for discovered immutability.

1.4 Choice of Methodology

To answer the problem statement in section 1.2 we base our results and conclusions on a scientific study using empirical data. We begin by investigating the problem statement with a literature study of immutability and the Scala programming language. The literature study conducted will give a bet- ter understanding of the problem and to implement an analysis for the empirical evaluation. Gathered literature lay the foundation of the theoretical chapters, related work and the final implementation of the project. The literature study is followed by the implementation of an immutability analysis that can evaluate Scala source code and output empirical data. Scientific validity will be achieved by the empir- ical data and evaluation conducted gathered from Scala source code and conclusions of this thesis will be based on that scientific study. 4 CHAPTER 1. INTRODUCTION

1.5 Delimitations

This project does not aim to extend the Scala compiler’s type system to have better built-in support for immutability. A well defined and simple immutability analysis will instead be developed for the empirical case study. We do not consider to create anything that can be used in practice for the Scala compiler or as a tool for developers to use on a daily basis. The performance of the developed analysis will thus not be of concern in this project, but it should, however, be usable to do the evaluation. The amount of source code analyzed for the empirical study depends on the time frame of the project.

1.6 Societal, Sustainability and Ethical Aspects

While the actual results and goals of this project have little impact on sustainability, societal and ethical aspects, there are related areas that can utilize it with regards to these aspects. As today’s modern soci- ety relies on programming and programs for many crucial tasks, the role of software development and design has become exceedingly more important. The alleged benefits of using immutable data by de- sign in programs or immutability in programming languages can, therefore, touch upon many different societal aspects. One frequent argument is that utilizing immutable data provides more maintainable code (e.g, increased testability) which has several economical aspects to it. With maintainable code, resources can be spent on developing newer functionalities, optimizations and innovations in less time that could reduce the development cost. Performance gains and shorter running times are also with well-maintained code likely to arise if prioritized and may have positive effects on the environment and sustainability depending on the application. The empirical study relies on publicly available open-sourced code that is not unethical to use and does, for example, not contain any proprietary or sensitive data. The study does not reveal or make any claims to what the quality of the code is with regards to, for example, maintainability and it is im- portant to recognize the intellectual property of the code and not take any credit for it. Furthermore, the insights given from this study may influence researchers or language designers in their decisions with regards to immutability. These design decisions could make immutability easier or harder to use, which in turn affects how many people that can learn and use it on an ethical aspect. It is therefore together with other aspects important for others to be able to validate and reproduce the results from this study. The results of this project are hence open-sourced and can be found in the appendix A to let anyone access and verify it. Chapter 2

Background

The purpose of this chapter is to give a theoretical background that is relevant to this project and needed to understand the problem explored. We begin by briefly explaining key features of the Scala program- ming language and touch upon concepts of object-oriented programming. Ideas of functional program- ming are then discussed followed by a detailed explanation of immutability and immutability analysis.

2.1 The Scala Programming Language

This section gives a short introduction to the Scala programming language and focus on a few key as- pects of it related to this project. Scala is an acronym for “Scalable Language” and is a popular functional and object-oriented pro- gramming language with a strong static type system that was released publicly in 2004 [1]. The name “Scalable” comes from the idea that the language should scale and grow with the programmer’s needs. The language is designed to be high-level with a concise syntax that allows programmers to do complex tasks with less amount of code and while it is still easy to understand. The majority of programs writ- ten in Scala are compiled to bytecode and executed on the Java Virtual Machine (JVM). Recent projects, for example, Scala.js [5] and Scala Native [6] allow you to compile Scala code to run outside the JVM. Because of Scala’s relation with Java and the JVM, Scala is featured to have seamless inter- operability with Java. The Scala compiler allow developers to mix both Java and Scala classes [1] and it is possible to use Java libraries and call Java methods in Scala programs with no special syntax. Scala re-uses much of what is already written in Java without the Scala programmer’s knowledge, for exam- ple, Java’s primitive types, under the hood. Two of the key features of the language are that Scala is an object-oriented language and full-fledged functional language and a very important aspect of Scala is that:

“Every value is an object and every operation is a method-call” [1].

A value is the result of any computation or expression in Scala. Because Scala is object-oriented and class-based, all values are therefore objects that are instances of classes. Functions are therefore ob- jects, and the function type is a class like any other value. The same goes for numerical values such as integers or doubles which are objects too. An example of this is the statement 1 + 2 where two inte- gers 1 and 2 are added together. The two integers are instances of the Int class and the operator + is a method-call on the object, the operation could thus be written as 1.+(2).

5 6 CHAPTER 2. BACKGROUND

2.1.1 Classes and Objects Programs that are written in an object-oriented programming language use objects that interact with each other at run-time to carry out tasks. Classes are used to generate objects and defines their behav- ior. An object is an instance of a class, and an object is created by instantiating a class at run-time. In Scala, a class is defined with the class keyword and classes are instantiated using the new keyword, creating an object.

class Hello { //Classdefinitiongoeshere private val greeting: String = ”Hello,World!”

def printGreeting = { println(greeting) } }

Listing 1: The class declaration of Hello in Scala.

Listing 1 shows the definition of a class Hello written in Scala. Using the Hello class you can create Hello objects with the new keyword by using the statement: new Hello. A class in Scala consist of fields and methods (functions). The fields hold data by using variables that refer to objects, and the variables are defined with the keywords val or var. Methods are defined by the keyword def and contain the ex- ecutable code. The methods of a class are often used to manipulate its fields to do some computation. When creating an object (instantiating a class), the runtime allocates memory and stores the data of the object. The assigned values for the fields (the data) of an object are called the object’s state. Depending on how classes are designed, classes and its instances can be made immutable. If the state of an object cannot change after it has been created, then we say that it is an immutable object. More about immutable objects and immutability in section 2.3.

2.1.2 Templates (class, object and trait) In the previous section 2.1.1 we used the notion of classes and objects that are related. The class is a key component in object-oriented programming and represents the source code that defines some behavior once, where an object is an instance of a class at run-time that can be initialized several times. Beside the ordinary concept of object-oriented classes, Scala has different templates [2] called class, object and trait, that can be used to create different types of classes. We also have the notion of case (for objects and classes), anonymous and different modifiers, for example, abstract and private that can be used on these templates. CHAPTER 2. BACKGROUND 7

The reason that the class types are brought up and explained here is because the analysis is going to treat them separately and check how each type differs in immutability. Here is a short description of the template keywords:

class: The class keyword is used to create an ordinary class as described in section 2.1.1.

object: The object keyword is used to define a singleton object (a common design pattern in object- oriented programming). A Scala object is a type of class that can only be initialized once and there is thus at most only one instance of that object. The object is automatically initialized the first time it is used.

trait: The trait keyword can briefly be described to create a type of class that cannot be instanti- ated but can be used for inheritance. Classes, objects and traits can extend multiple traits but only one class at the time.

case class and case object: The case class and case object are two types of classes that automat- ically support pattern matching by implementing helping methods by default, for example, equals and copy. These are often used to to compare and store data, and the generated functionality re- duces boilerplate code.

There is also the concept of anonymous class that is a synthetic subclass generated by the Scala compiler.

class Person { var name: String = ”” } // The variable ”p” is an instance of an anonymous subclass val p = new Person { name = ”Tycho” def nameLength = name.length }

Listing 2: An example of an anonymous class that is a subclass of Person that implements a method called nameLength.

Listing 2 shows an example of an anonymous class that. Anonymous classes are often used by the user to create a subclass for some very specific task that will never be re-used, for example, when a specific instance of a class is needed as an argument of a function. 8 CHAPTER 2. BACKGROUND

trait Immutability case object Immutable extends Immutability case class Mutable(val reason: String) extends Immutability class ImmutabilityAnalyzer(val sourceCodes: List[File]) { sourceCodes.foreach(analyze(_)) // The immutability analysis def analyze(file: File) = { val immutability = getImmutability(file) immutability match { case Mutable => ...... } } } object Program { // The starting-point of the program def main(args: Array[String]): Unit = { val sourceCodeDir = args(1) val sourceCode = new File(sourceCodeDir) .listFiles .toList new ImmutabilityAnalyzer(sourceCode) } }

Listing 3: Example pseudo code of the different templates of Scala representing an immutability analyzer.

Listing 3 is an example program that ties together the different templates (class types), trait, case object, case class, class and object, and represents a sample program of an immutability analysis. The object program is a singleton that represents the starting point of the application with its main method, which is reasonable since we only want to have one point of entry. The main method in the program uses an instance of the class called ImmutabilityAnalyzer to run analysis on given files. The returned immutability from the file is compared with pattern matching using the case class Mutable and case object Immutable that both inherit the trait Immutability. CHAPTER 2. BACKGROUND 9

2.1.3 Types In statically-typed programming languages, the type of a variable is known at compile-time with the use of a static type system [7]. The task of the system is to classify variables and expressions with re- gards to the kind of value they can refer to or compute at run-time. Knowing the type of a variable makes it possible to avoid certain run-time errors. For example, multiplying two types that would not compute can be detected at compile-time instead of giving a run-time error when evaluated. Static type systems can also detect if the correct number of arguments is passed to a function or that private vari- ables are never accessed outside of their scope. The Scala compiler has type checking phase that verifies and enforces the type of a variable. The type of the variable limits and restricts the possible values of what it can refer to or compute at run- time. “Every variable and expression in a Scala program has a type that is known at compile-time” [1]. In Scala, there is a distinction between a template and a type. For example, a class is not a type because a class can construct many types with the use of type parameters. The generic class, Queue de- fined as, class Queue[T] has a type parameter T that can be instantiated with many different types. In- stantiating Queue with the type String and Int would result in the two types Queue[String] and Queue[Int], but not in the type Queue that is a class.

Figure 2.1: The Scala class hierarchy. [8]

The diagram in figure 2.1 illustrate the class hierarchy in Scala. The class that all classes inherits from is Any that has two subclasses AnyVal and AnyRef. These two subclasses represent two different class categories. All classes belonging to AnyVal are predefined “value” classes in Scala. The other category contains reference classes where all user-defined classes belong, any user-defined class extends AnyRef. 10 CHAPTER 2. BACKGROUND

2.2 Functional Programming

The functional way of programming is essentially to write programs that use functions without side- effects to evaluate expressions whenever possible. We say that a function has no side-effect when it does not mutate any state and we also call it a pure function. This is opposed to the imperative way of programming where the program is a sequence of statements where each statement often mutates some state in the program to do some task. Naturally, to avoid side-effects, a functional programming style use immutable data to avoid mutations from taking place. In a purely functional programming language, a program would be defined as a function composed entirely out of other functions [9]. This is not entirely feasible in practice for all programs, and some side-effects are inevitable, and that is why functional programming languages often allow mutations in some way or another.

def square(x: Int) = x * x

Listing 4: A function that calculates and returns the square of a number x without any side-effect.

A pure function without side-effects does not depend on any state outside the function and cannot mutate any as seen in listing 4. Having functions with no side-effects means that order of execution does not matter [9] and any function can be evaluated at any time and is guaranteed to return the same output with an input. Each function would take some input and return some output with no other effect than to compute the output. Functions with side-effects, on the other hand, do modify some state of the program. A function could, for example, change a variable that is shared with other functionality and mutate a state.

private var y: Integer = 0 def square(x: Int) = { y = x * x y } def isZero() = { y == 0 }

Listing 5: A function with a side-effect that calculates the square of a number x and stores it in a vari- able y outside the function (the side-effect).

As shown in listing 5, the side-effect of the function square is that the variable y is mutated on ex- ecution. It is not obvious to the programmer that when we call the square function also mutates the state. Here the state of the object is changed other than just returning a value, making the function isZero return another value than previously. Functional programming languages have features that help to program in a functional style. One considered important feature is to have so-called first-class and higher-order functions which mean that functions are treated as any other value. Functions should be able to be passed to functions, return from functions and be stored as data. To tell if a function has side-effects in Scala the result of returned from the function is of type Unit. A function that does not return any value would not do anything in the program unless it did with some side-effect. CHAPTER 2. BACKGROUND 11

2.3 Immutability

When something is immutable, we say that it cannot be changed or that it is unchangeable. The defi- nition of an immutable object in an object-oriented programming language is an object that has a state that cannot be mutated once instantiated (created). Moreover, an immutable class is a class whose in- stances cannot be mutated, meaning that there are no methods in the class that can mutate an instance of it. Classes that are not immutable can, however, have instances of it that are immutable. An exam- ple of this is having the class List[T] that can be instantiated with both an immutable type and muta- ble type T. In a functional programming style the program naturally use immutable data and functions without side effects see section 2.2.

2.3.1 Why Immutability Matters There are benefits and trade-offs of using immutable objects, some of which we have already listed in 1. One benefit is that it may be easier to reason about immutable objects compared to mutable objects because they are generally simpler because of their lack of state. Immutable objects cannot change their state over time and only have one state, and that is the state it was created in. A mutable ob- ject, on the other hand, can change over time and differently depending on how it is used making it more complex and potentially more difficult to have an understanding of. Immutable objects are good for multi-threaded environments as they are thread-safe: two threads that concurrently access an im- mutable object cannot cause race conditions because no state can be modified. Immutable objects can also be shared freely between functions. Passing a mutable object to a function may in the worst case require a defensive copy to be made first to ensure that the state of the object does not change unin- tentionally inside the function. That would not be an issue with an immutable object because its state cannot be mutated inside the function. The most apparent trade-off when using immutable objects is the performance cost of needing to create a whole new object to change single state value. Instead of creating a whole new modified copy of the object, it is simpler and intuitive to mutate the existing object. One may argue that it is a more natural way of doing things, we often mutate existing material in the real world instead of creating new material from scratch. From the book, “Effective Java” by Joshua Bloch “Classes should be immutable unless there’s a very good reason to make them mutable” and “If a class cannot be made immutable, limit its muta- bility as much as possible.” [10]. By limiting mutability, the number of states in that an object or an application can exist in is reduced. With fewer states it is easier to reason about that object and errors are more likely to be reduced [10]. 12 CHAPTER 2. BACKGROUND

2.3.2 Varieties of Immutability There are different varieties of immutability used in practice today, and this section lists some of them to give an overview of each concept based on Potanin et al. [3]. Some of these varieties are used when implementing an immutability analysis for this project.

Object immutability An immutable object is an object that cannot be modified (mutated), i.e., its state cannot be mutated.

Class immutability When every instantiated object of a class is immutable, then we say that the class itself is immutable.

Deep and shallow immutability (transitivity) Immutability can be deep or shallow, i.e., transitive or non-transitive:

• Deep (transitive) immutability means that all objects referred to by an immutable object must also be immutable.

• Shallow (non-transitive) immutability has a more relaxed constraint, and the immutable ob- ject may refer to objects that are mutable, but the fields of the immutable object itself cannot be mutated.

Reference immutability (read-only references) Languages with the support of reference immutability have the notion of read-only references. A read- only reference cannot mutate the object it is referring to. When all references to an object are read- only, then nothing can mutate the object, and the object is immutable. There are, however, often no guarantee that a referred to object is immutable because the object may still be mutated by some other reference that is not read-only, unless there is some analysis that ensures that there only exist ready- only references to that object [11]. Reference immutability thus often ensure shallow (non-transitive) immutability and “reference immutability” does not mean that a reference itself is immutable.

Non-assignability Non-assignability is a form of immutability and property of a variable that makes sure that it cannot be reassigned. Since fields of objects are variables and if no variable can be reassigned after its initial assignment, the object is effectively immutable if what the fields are referring to is immutable. This make non-assignability give shallow (non-transitive) immutability too. Assignment of an object’s field mutates the object, but it does not mutate what was previously on the field or what was assigned to the field.

Concrete and abstract immutability Concrete immutability does not allow any change to the object’s state. Abstract immutability, however, allows an immutable object to change its internal state but not the object’s “abstract” value. The object is still immutable from the perspective of the object’s observer that can only see the abstract value, but the object may mutate internally. This can, for example, be useful to speed up certain operations, and buffering. CHAPTER 2. BACKGROUND 13

class Cache { private var cached: Boolean = false private var theValue: Integer = 0 def getValue() = { if (!cached) { theValue = someLongComputation() cached = true } theValue } def someLongComputation(): Integer = { 42 } }

Listing 6: A class where instantiated objects have abstract immutability behavior. An object of Cache would first compute the value once and return it; any subsequence call would only return the value (the abstract state).

An example of this is shown in listing 6. No observable mutation is visible for the user, but the object’s of the class Cache do mutate internally by setting the cached and theValue field. The abstract value of the class does not change since the same value of the function always returned.

2.3.3 Immutability in Scala Scala programs can achieve all of the varieties of immutability, beside reference immutability, listed in 2.3.2 with the correct use of two variables, var and val for fields. A variable in Scala is an entity that refers to an object:

• val: Once initialized, the variable can never be reassigned, and the reference is read-only through- out execution. The object referred to by the variable may, however, still be mutable.

• var: Can be reassigned and refer to different objects after initialization and throughout execu- tion, effectively making it mutable.

Programmers need to use val with care to achieve deep or shallow immutability to ensure that an object’s state cannot be mutated at run-time. The use of val is highly encouraged and considered good practice. Arguments of functions are, for example, by default val instead of var and programmers do not have to worry about and determine if the passed arguments to functions are reassigned, something that would be needed if var was used. Also, as described in “Programming in Scala” by Martin Oder- sky (the designer of Scala):

“Prefer vals, immutable objects, and methods without side effects. Reach for them first. Use vars, mutable objects, and methods with side effects when you have a specific need and justification for them.” [1]

There are also two access modifiers, private and protected, that can be used to restrict access to variables for certain scopes of the code. Listing 6 displays the class Cache that uses two private vars. 14 CHAPTER 2. BACKGROUND

The meaning of private here is that those variables can only be accesses within the class Cache. Ob- ject’s of Cache can thus not be mutated at run-time from other objects. The other modifier protected make variables accessible only from the class itself and children of the class. The scope of private can be altered to allow certain packages or classes to have access to them with the syntax private[X] and protected[X] where X is a class, package or object. These access modifiers can be used to create. for example, different versions of abstract immutability, by never exposing a var field as shown with the caching example of listing 6.

2.4 Immutability Analysis

The goal of immutability analysis in object-oriented programming languages is to figure out the im- mutability of a class or an instance of a class. Reasons for wanting to do immutability analysis can be to verify if a class is mutable or find out the cause of why it is. It may be that the reason for mutability is unintended or unnecessary and removing the cause would then make the program more simple (less complex) in turn. An analysis can be done statically, without executing the program in question, or dynamically, by analyzing it under execution. In this project, a static immutability analysis will be con- ducted by analyzing the source code of Scala projects. One reason for making a static analysis tool is that it can be automated to accompany a developer aiming to write code using principles of immutabil- ity without needing to execute the program. The dynamic approach, on the other hand would require the program to execute and the result of the analysis may depend on how the program is executed. All executable parts of a program may not run, and some programs may be hard to execute and get any good information from.

2.4.1 Static Program Analysis Because programs written in Scala are compiled to Java bytecode and executed on the we have two options when it comes to statically analyzing Scala programs source code. Either we analyze the pro- gram’s plain Scala source code or its bytecode after it has been compiled. An issue with analyzing bytecode is that it does not have all the details that source code has. Scala source code gets throughout the compilation process translated to a more Java-like language and at last to the actual bytecode. The bytecode may, for example, contain optimizations or code that the user has not written. This means that it would, for example, be hard to know which template we are ana- lyzing with bytecode, i.e., if it is an object, trait or class because they all end up as a Java class in the bytecode. These templates are abstractions at the Scala-level and the underlying bytecode only uses available instructions for the that does, for example, not include instructions for trait specifically. Another important aspect when it comes to immutability analysis is that code written and fulfill a certain immutability property may be translated and optimized to something that looks mutable in bytecode. For example, the non-reassignable field lazy val would be translated by the compiler to something mutable, and its class would become mutable, even if it is not mutable from the developer’s point of view. CHAPTER 2. BACKGROUND 15

2.4.2 Scala Compiler Plug-in The Scala compiler is designed in a way that makes it possible to create and use plug-ins without mod- ifying the compiler itself. A plug-in is an external component with some extra functionality that the Scala compiler load utilize it. This allows users to create and distribute plug-ins with functionality without making any changes to the Scala compiler. An example of a plug-in can be extending the com- piler with functionality to read extra annotations and type-checking information or add an entirely new phase of the compiler after an internal compiler phase has completed. A static program analysis, in the form of a compiler plug-in for Scala, is created for this project. The plug-in reads Scala source code, and output information about the immutability of detected classes, more details about the plug-in can be found in chapter 4. To achieve the extra functionality that is suitable for a compiler plug-in a form of metaprogram- ming is often used. Metaprogramming refers to and is briefly about writing meta-programs. Meta- programs can, for example, be compilers, interpreters and program analyzers, programs that manipu- late itself or other programs [12]. A key concept of metaprogramming is called Reflection that can be used to observe and manipulate the behavior of programs at compile time or when they are executed. In Scala there a Reflection framework (the package scala.reflect) that can be used to view and modify, for example, fields and methods of templates. Listing 7 shows an example of a compiler plug-in that if loaded by the Scala compiler would output “Class is immutable” if an immutable class is detected. The plug-in implements a new phase called “immutability analysis” and runs after Scala compiler phase called “refchecks”1 that is one out of many compiler phases. The plug-in can utilize these different compiler phases to achieve different results, running after the plug-in after or before an optimization phase for example.

1https://wiki.scala-lang.org/display/SIW/Overview+of+Compiler+Phases 16 CHAPTER 2. BACKGROUND

class ImmutabilityAnalysis(val global: Global) extends Plugin { import global._ val name = ”immutability analysis” val description = ”figures out the immutability of templates” val components = List[PluginComponent](Component) private object Component extends PluginComponent { val runsAfter = ”refchecks” def newPhase(_prev: Phase) = new

,→ ImmutabilityAnalysisPhase(_prev) def isImmutable(t: Type): Boolean = { // Return true if immutable } // Traverse through source code and check immutability class ImmutabilityTraverser() extends Traverser { override def traverse(tree: Tree): Unit = tree match { case cls@ClassDef(mods, name, tparams, impl) => // We matched with a class val klass = cls.symbol if (isImmutable(klass.tpe)) { // Do something if immutable println(s”Class \$klass is immutable”) } traverse(impl) case _ => // We did not match a class, traverse through the tree super.traverse(tree) } } // Immutability analysis phase class ImmutabilityAnalysisPhase(prev: Phase) extends

,→ StdPhase(prev) { override def name = ImmutabilityAnalysis.this.name def apply(unit: CompilationUnit) { val immutabilityTraverser = new ImmutabilityTraverser() // Traverse through all source code immutabilityTraverser.traverse(unit.body) } } } }

Listing 7: Example code of a Scala compiler plug-in implementing an immutability analysis. Chapter 3

Related Work

This chapter highlights relevant research conducted related to this project’s area. A number of research papers are discussed in the area of immutability, some of which are recent in-depth comparisons and discussions about immutability.

3.1 Immutability Support in Programming Languages

As explained in section 2.3, there is in general no doubt that using immutability is in many cases con- sidered good design when programming. Immutable data can, for example, yield simpler states, thread- safety and less concern when data is shared. Potanin et al. [3] investigate what immutability is and how it is implemented in today’s “main- stream programming languages” (e.g., Java and C++). They provide an excellent summarization of recent research conducted in the area of immutability and object-oriented languages. One thing they point out is that programming languages that are non-functional have very limited support for im- mutability. They suggest that the two major improvements by recent immutability research and pro- posals of the last decade are: • “Support transitive read-only by supporting the enforcement of not just the reference to an object but also any further references from such object being read-only.” • “Support for object immutability rather than just read-only references thus guaranteeing that no unexpected alias to an immutable object can change its state.” The first point ensures that the references of an object, that is referred to by a read-only reference, are also read-only. Having only a read-only reference to an object does, however, not mean that the object cannot be referred to by another reference that has write access. The second point introduces object immutability that in turn guarantee that none of those write access references can exist. Section 3.2 will in more detail explain and show how these two points can be achieved with type systems. There is wide variety of ways of having immutability support in languages. Much of the support have little empirical data on what software engineers actually wants in practice according to Coblenz et al. [4]. They reviewed existing literature about immutability and developed a classification system. Their classification system showed mutability restrictions for selected languages and type systems in detail. They also interviewed developers and conclude that immutability is an important feature in practice, but support for it lacks in commonly used programming languages. They point out that their empirical study of interviewing programmers is not significant enough and “significantly more work will be required in this area if we are to base language design decisions on empirical data.”, what an empirical study on source code like this project contributes with is therefore interesting.

17 18 CHAPTER 3. RELATED WORK

3.2 Immutability with Type Systems

One way of achieving immutability in programming languages is to let the type system enforce rules of immutability. Having the compiler guarantee immutability can, for example, be useful for enabling optimizations and documentation. Birka and Ernst [11] introduced a type system that enables reference immutability or so-called read-only references (see section 2.3.2). The type system does compile-time verification and run-time checks for reference immutability and can be used for essentially any object-oriented programming language. They have created a working implementation of the type system for the Java programming language. The implementation is called Javari (Java with reference immutability), and it was used and tested in practice for usability and efficiency. Javari has keywords and additional annotations to vari- able declarations in Java, the keyword read-only is, for example, checked by the type system. The new annotations ensure that no read-only reference can be mutated or assigned to by the Javari compiler. According to them, it is more flexible to ensure reference immutability rather than object immutability. Reference immutability allows objects to be mutated during a construction phase and not after that. Having reference immutability can lead to object immutability if properly used. If all references to an object are read-only references, then the object cannot be mutated and is immutable. They conclude that reference immutability is something to consider to support when designing a programming lan- guage. Case studies have shown that Javari is practical to use and enforce reference immutability. How- ever, as pointed out by Quinonez et al. [13] Javari only type-check references that use the Javari type annotations and not original Java code. To solve this usability issue, they developed an algorithm that automatically infers reference immutability for Javari and a tool called Javarifier. The tool Javarifier can automatically convert Java libraries to be compatible with Javari and infers mutability for refer- ences. They showed that the tool could infer mutability in Java programs with one hundred thousand lines of code. This allows developers to use Javari without manually converting existing code, which may be tedious and error-prone. We could, however, not find any open sourced code of Javari being used in practice. Building on Java’s generics and annotations Zibin et al. [14] introduced a language extension for Java called Immutability Generic Java (IGJ). IGJ ensure both reference immutability and object im- mutability where an object is either mutable or immutable. The terminology of IGJ is similar to Javari but the guarantee of object immutability allow for stronger immutability properties. Without changing Java’s syntax they have, for example, added a new type argument (the immutability argument) to the beginning of the type arguments list. Three types of references Mutable, Immutable and ReadOnly can be used. CHAPTER 3. RELATED WORK 19

class ListContains { private Integer id; private List list; public @ReadOnly boolean contains(X x) { ... } public @Immutable Integer getId() { return this.id; } public @Mutable Integer setId(Integer id) { this.id = id; } }

Listing 8: The definition of a Java class ListContains written using IGJ annotations and generics.

Listing 8 show an example of a Java class definition using IGJ. The class has a private field list that is an Immutable list defined by the first type parameter in the declaration. Annotations are used to specify the immutability of this inside a method, for example, @ReadOnly and @Immutable as seen in the ListContains class. As seen in section 2.3.1 one of the benefits of having immutable objects is that they are inherently thread-safe. Concurrent programs have to make sure that two threads cannot be affected by side-effects from each other. A type system developed by Gordon et al. [15] was designed to prevent unintended side-effects in concurrent programs. They too introduce reference immutability to control the muta- tion of objects together with the notion of isolation. Their type system introduces four type qualifiers: writeable, readable, immutable and isolated. The read-only type qualifiers are readable and immutable, references with these qualifiers are not allowed to mutate its referent (it is transitive). The isolated type qualifier ensures that the reference is unique to what they call an “externally-unique object cluster” that is a graph of objects.

Figure 3.1: A figure that shows an isolated reference and an immutable reference referring to graph of objects [15].

Figure 3.1 is a figure taken from the paper that shows how the isolated reference uniquely refers to a graph of objects. The objects inside the graph all refer to each other freely, and it is allowed for the objects to also refer to immutable objects outside the graph (adds flexibility). It is, however, not al- lowed to refer to any of the objects inside the object graph because all objects that are reachable from an isolated reference must pass through that isolated reference. References that are isolated are good 20 CHAPTER 3. RELATED WORK for parallelism as mutable data can be grouped amongst threads. Because there is also only one ref- erence to the graph of objects it can easily be converted to other type qualifiers such as writeable or immutable. The flexibility is said to be good for algorithms that utilize parallelism to update and de- stroy objects. They test the type system with an implementation of it in the programming language C# that was in active use by a Microsoft team. The team at Microsoft were satisfied with the type system and the benefits of reference immutability, but in particular, the provided safe parallelism. Pechtchanski and Sarkar [16] introduce a specification for immutability. The immutability speci- fication is then applied and discussed with regards to code optimization. The specification has a set of “immutability assertions” and an assertion expresses an immutability property. These properties are ex- pressed in three dimensions, lifetime, reachability and context. Immutability assertions can with the life- time dimension specify the duration for when an object is immutable and in the reachability dimension specify deep or shallow immutability. Assertions in the context dimension can, for example, specify that a field F is deeply immutable in a specific method. The field F can thus be mutated in some other context in another method or statement in a program. They show how these immutability assertions can be used to improve the effectiveness of code optimization, for example, in global value number- ing, load elimination, elimination of null pointer checks and array bounds check, loop-invariant code motion, dependency analysis, data transformations. They implement the specification as a framework for Java and showed promising results of optimizations, some benchmarks had 5-10% improvement in speed.

3.3 Immutability Analysis

As we touched upon in section 2.4, immutability analysis attempts to answer the question whether a class or an object fulfill some immutability property. For example, given a class definition, we can an- alyze if it is a class whose instance can never be mutated after construction and conclude that the class is deeply immutable. Dynamic and static code analysis are two methodologies used to conduct im- mutability analysis, the latter requires the program under analysis to be executed were the former do not.

3.3.1 Static Code Analysis Static code analysis or source code and byte code analysis for Scala is a methodology where we analyze code without executing it. Instead of executing the program, we analyze its code either by reading the bytecode or the source code of it. An extensive framework for static code analysis is OPAL [17] that have implemented different Java bytecode analyses. One analysis mode that is of great interest for this project is OPAL’s immutability analysis. Their immutability analysis specifies two types of immutability, object immutability, and type im- mutability. The first one, object immutability, define a mutability rating of an instance of a class. They have defined three different ratings that are “mutable”, “immutable” (deeply immutable) and “condi- tionally immutable”. The first two ratings are very similar to their corresponding definition listed in 2.3 and conditionally immutable is a new concept. When an instance of a class is conditionally immutable, then the instance of the class cannot be mutated, but that instance can itself refer to other objects that may be mutated. A typical example of something that is conditionally immutable is an immutable col- lection that may contain mutable objects. The analysis looks at the bytecode of classes and investigates their fields to figure to define a mutability rating. The second type of immutability, type immutability, investigate if all instances of a type and gives one of the three mutability ratings to the type (it could be the type of a class). CHAPTER 3. RELATED WORK 21

Any related work that do source code analysis as explored in this project were found, it does, how- ever, not mean that there are none.

3.4 An Empirical Study on C++ const and Immutability

As we have discovered previously in section 3.2 much research has been done in designing type sys- tems that deal with immutability. These type systems can for example guarantee deep (transitive) im- mutability. However, none of these studies focus on what developers actually do in practice with the immutability support we have in languages available today. Eyolofson and Lam [18] did an empirical study of immutability usage with regards to the C++ programming language with a developed dynamic analysis tool. The keyword const is a type qualifier in C++ that can be used to specify immutability. When const is used on a method of a class then mutating the fields of the class (in context of the object) from that method is not allowed. Moreover, when const is used on a reference, it specifies that no mutation can happen through that reference (we can only read it and not write to it). These two cases of const (on methods and references) indicate that const give developers shallow immutability by default. Remem- ber, shallow mutability means that fields of an object cannot be mutated. To have deep immutability, we also need to ensure that any object referenced through the fields cannot be mutated, which const does not do. As we explored in section 2.3.2 about varieties of immutability, there is also the notion of abstract and concrete immutability that is used in practice. Having concrete immutability essentially means that no state of the object is mutated. Abstract immutability, however, say that there should not be any ob- servable mutation to the object, the internal state may be mutated. Concrete immutability is the default behavior given by const and to get abstract immutability in C++ there are escape hatches to do it. One can use the mutable keyword on a field that makes it possible for const qualified methods to mutate the field. This feature also makes concrete immutability not guaranteed, since any const qualified method may mutate fields using the mutable keyword. In their study, they observed how developers utilize const with 7 different large C++ projects rang- ing from 13 to 214 thousand lines of code. Their goal was to explore possible meanings of immutabil- ity and what immutability guarantees developers are expecting in practice. The hypothesis to why de- velopers would use abstract immutability is, for example, buffering, caching and delayed initialization. They created a dynamic analysis tool called ConstSanitizer that was used to track how const was used and why. Their approach was to modify the way projects were built to use their tool and conduct the analysis by running the project’s test suite. The tool tracked mutations performed on const qualified objects or references which they call “writes-through-const”. Either a writes-through-const happen transitively (through a field which is allowed) or through some of the C++’s const escape hatches. To evaluate the projects they compiled them with ConstSanitizer and ran the compiled executable that gen- erated a report of writes-through-const. They then classified the writes-through-const how the write occurred and why the write occurred. They discovered that developer does write-through-const in practice and observed 17 unique cases. Developers did write-through-const by violating shallow immutability (using mutable keyword) and deep immutability (by mutating objects referred to in fields) equally. Deep and shallow immutability was violated 8 times each in the 17 cases. Half or 9 out of 17 cases of the writes-through-const were not abstract immutable and had observable mutations (which was classified as incorrect). 22 CHAPTER 3. RELATED WORK

3.5 Tools for Immutability

Developing programs in object-oriented programming languages often yield in a mix of both mutable and immutable classes. The nature of many programming languages makes it easy for many developers to mutate state, something that might be unnecessary. Striving to have immutable classes can be a good design choice to, as we have seen, avoid reasoning about side-effects. Kjolstad et al. [19] present an algorithm that can transform a mutable class into an immutable class. The tool proved to be less error prone than doing manually transforming classes and it was suc- cessful in most cases. They showed that the tool potentially saved a programmer from doing non-trivial changes (analyzing many methods) and about 45 lines of code on average per class on their test data. Instead of transforming existing code, there are also tools that can help developers write code using immutability principles. For example, an editor used to write code can notify the developer if a class was detected to become mutable after some code change. A program that works this way is called MutabilityDetector [20] that analyze Java classes and detects if they are immutable. By annotating the classes with, for example, Immutable, the program would detect if the class became mutable using a plug-in for the developer’s editor. The tool inspects the fields of the class and checks if they are, for example, reassignable.

3.6 Summary

As we have discovered in this chapter, research has been done in designing type systems that deal with immutability. These type systems can, among other things, guarantee deep (transitive) immutability. There have, however, not been much research on what developers actually do in practice, with the im- mutability support in languages used today. Furthermore, there are, to our knowledge, no study today about immutability usage in the Scala programming language. One reason for it might be because im- mutability analysis is a complex task when classes can be mutated in many different ways. The goal is thus to define a simple immutability analysis and evaluate it. Chapter 4

Methodology

This chapter describes in detail how the goal of the degree project was achieved, the implementation of an immutability analysis, in the form of a compiler plug-in, and how it was evaluated.

4.1 The Immutability Analysis

This section describes how we developed an immutability analysis in the form of a compiler plug-in. The developed plug-in analyzes source code and assigns immutability properties to templates of a Scala program. A class declaration in Scala consist of fields and methods, and in immutability analysis, the question is how these statements define the immutability of an instance of that class. For example, if no instance of that class can be mutated after initialization, we call the class immutable. The analysis reads and analyzes source code during the compilation phase of a program and prints out the immutability information about the analyzed program. It was implemented as a compiler plug- in for the Scala compiler, utilizing the Scala reflection library and a programming model called Reac- tive Async [21]. The programming model Reactive Async is mainly designed for handling concurrency in programs, but it also has support for resolving cyclic dependencies, something that is crucial for our immutability analysis. The plug-in is made to execute in between compiler phases and after a phase called refchecks. At this point, the compiler has, among other things, throughout different phases parsed the code into an AST and type-checked it. The code has been transformed to have the information we need in order to do the analysis, a later phase than refchecks may not work as needed information gets lost at later phases. For example, the last phase called jvm transforms the source code to bytecode and class files. As seen in figure 4.1, the compiler plug-in is one of the phases the Scala compiler executes when com- piling a program.

23 24 CHAPTER 4. METHODOLOGY

Scala compiler

Phase 1 Phase 2 Immutability Phase n-1 Phase n Scala source Java bytecode analysis code

Traversal of Java Virtual Machine Analysis Report statistics typed AST (JVM)

Figure 4.1: An overview of the compilation process

The Reactive Async model utilizes the notion of a cell, which holds a value, and a corresponding cell completer to perform updates on the cell. The cell can be assigned values from a lattice designed to make the assignments monotonic and deterministic.A lattice has a defined join operator that returns the next value of the cell given the current and the new value. In our analysis, a cell is assigned to each template (class, trait, or object declaration) found and the values of the lattice are immutability proper- ties. Cells can have dependencies between each other so that an update of one cell may trigger an up- date of another cell. These updates can be conditional and setup so that only if the original cell update fulfill some condition we update another cell. Conditional updates are used for handling dependencies between templates and explained in more detail in section 4.1.2.

4.1.1 Immutability Properties With knowledge gathered from chapter 2 and 3, we list the immutability properties deemed necessary to create a simple but useful analysis that can decide the immutability property of a template. The im- mutability properties are:

Mutable: We give a template the mutable property if an instance of that template can be mutated directly or indirectly.

Shallow immutable: A template has the shallow (non-transitive) immutable property if the tem- plate does not have fields that can be reassigned, but has references to other objects that may be mutated (are shallow immutable or mutable).

Deeply immutable: A template is deeply (transitive) immutable if all instances of that template cannot be mutated after initialization.

Conditionally deeply immutable: The conditionally deeply immutable property is given to a template that is deeply immutable but depends on some other potentially mutable type. An exam- ple of this is a generic collection that can store different types, and the collection itself is declared in a way so that it cannot be mutated, but the type that is used with the collection may be shallow immutable or mutable (an example of this is shown in listing 9). CHAPTER 4. METHODOLOGY 25

As we have discovered in previous chapters, there are different types and properties of immutabil- ity. One immutability property not considered by our analysis is the notion of abstract immutability [3]. The reasoning behind it is that abstract immutability is not simple enough, and to compute abstract im- mutability one would have to deal with a class’s internal state which could be set up in a multitude of different ways and track state changes [18].

4.1.2 Detecting Immutability This section details how the plug-in detects and defines the immutability properties. The plug-in con- sists of three different steps that are executed in between compiler phases and after a compiler phase called refchecks. Inside the plug-in, using the Reactive Async model, each template is assigned a cell, and the lattice contains the different immutability properties. The values of the lattice are thus be muta- ble, shallow immutable, conditionally deeply immutable and deeply immutable (defined in section 4.1.1). The initial value of a cell is immutable and the methodology is to find proof that the class has another immutability property. The first step of our plug-in traverses every AST and creates a cell and cell completer for each template found. These cells and corresponding cell completers are stored in a map with bidirectional lookup between templates and cells that are needed for the below analysis. The next step is to analyze the fields and parents of each template found. First we check if the cur- rent immutability property of a field or parent yields a new immutability property for the analyzed tem- plate. We then set up a conditional update between the template’s cell and all the cells of the fields and parents templates. In this way, if the immutability property of a template cell changes, dependent cells will be notified and change accordingly (it may yield a new immutability property), an example of this is provided further below in listing 10 and 11. Each immutability property has a set of conditions that most hold. We determine a template to have one of the following immutability properties if the listed conditions of that property hold:

• We determine a template to have the mutable property if:

– The class contains any reassignable field, i.e., a var field definition. – The source code of the class is unknown and unreachable (see section 4.1.3). – It inherits from a mutable class or mixes in a mutable trait.

The property mutable is thus given by only inspecting the fields and the parents of a class.

• A template is given the property shallow immutable if:

– The template has only non-reassignable fields, i.e., val field definitions. – Has a parent that is shallow immutable. – A field has a type that is known to be mutable or shallow immutable. – A field has a type argument that is mutable or shallow immutable.

The use of a conditionally deeply immutable collection may be rated as shallow immutable, an example of this is shown below in listing 9.

• We give the property deeply immutable to a template if:

– All fields are non-reassignable, i.e., val definitions. 26 CHAPTER 4. METHODOLOGY

– The type of all fields is deeply immutable.

All known templates are by default assigned this property until another property holds.

• A template has the conditionally deeply immutable property if:

– It is deeply immutable with one or more type parameters.

An example of a conditionally deeply immutable template is a collection that can be used with any type of class. Listing 9 shows an example scenario where a conditionally deeply immutable class called List is used.

class Mutable(var argument: String) class ImportantTask { val test: List[Mutable] = new List(new Mutable) ... } class List[T](val argument: T) { val immutable: String = ”Immutable string” ... }

Listing 9: Example usage of a conditionally deeply immutable class List.

The class List[T] would be deeply immutable if it did not have the type argument T. The class can now be instantiated with a mutable or deeply immutable type T. In the example, the class Im- portantTask uses the List class with a type argument that is mutable, making the ImportantTask effectively shallow immutable.

When a template is deemed not to be deeply immutable, then the join operator of the immutability lattice is used. The join operator gives the highest precedence to the immutability property mutable followed by shallow immutable, conditionally deeply immutable and deeply immutable. A join between two properties returns the maximum of the, thus succeed only if one of the properties have a higher precedence. The mutable property is consequently final, and the join operator does not return any other immutability property. A template can, for example, first be assigned a property that holds at that point in time but later on become mutable. Dependencies between templates can be setup in many different ways and, for example, create cycles as shown in listing 10. When a template A uses another template B, we say that template A de- pends on template B. To draw any conclusion about the immutability property of template A, according to the properties above, we also need to know the the immutability property of B. In other words, we need to handle dependencies between templates. Here is where conditional updates in the Reactive Async model come into play. CHAPTER 4. METHODOLOGY 27

class A(val b: B) class B { var a: A = new A(this) }

Listing 10: Two classes A and B with a cyclic dependency.

We cannot assume that we know the immutability of B when we analyze A with a dependency of B and dependencies can be read in different orders in listing 10. To solve this, we added conditional updates using Reactive Async between the cells and a callback is fired when a change to a cell occurs. An example callback is shown in listing 11.

cellForA.whenNext(cellForB, (x: Immutability) => { if (x == Mutable || x == ShallowImmutable) { WhenNext } else { FalsePred } }, Some(ShallowImmutable))

Listing 11: When the cell cellForA changes a callback is given for cell cellForB.

For the code example of listing 10 the corresponding dependency callback in 11 may be created in a different version with another order, where the cells are cellForB to cellForAinstead. If we read class A first, we would detect class B with having the default deeply immutable property and setup a conditional update as in listing 11. When we then analyze class B we notice that it has a reassignable field, giving class B the mutable property. A callback is then fired for class A and the if statement re- turns WhenNext meaning that the cell for class A should receive an update. The third argument of the whenNext method is the value it should receive on update, which in this case is ShallowImmutable and class A is given the shallow immutable property. If the callback would return FalsePred then the cell for class A would not update with a new value. The third and last step for the plug-in is to print out all the statistics gathered and this is done when all dependencies between templates have been resolved.

4.1.3 Limitations The plug-in described in this section is not designed to handle external or unknown dependencies. If the project analyzed by the plug-in finds a template that depends on some other external template, then we have to assume that the immutability property is mutable. This is because we can, in most cases, not know the immutability property of the external dependency. The source code of an external de- pendency might be proprietary and unavailable. This includes Scala code that uses Java classes as well, because these are not compiled by the Scala compiler and therefore never accessed by our plug-in. In the case of bytecode analysis, this would not be an issue, because the entire project’s bytecode also in- cludes the code from the external dependencies and can, therefore, be analyzed. 28 CHAPTER 4. METHODOLOGY

4.1.4 Immutability Assumptions Due to the limitation of not being able to handle external or unknown dependencies the developed plug-in is by default executing with some assumptions about the immutability of certain templates. The plug-in assume that primitive, crucial and known to be immutable templates are deeply immutable without analyzing them. This is to reduce the problem of having unknown external dependencies de- scribed in section 4.1.3. These templates are for example frequently used primitive Scala and Java classes such as Int, Boolean but also java.lang.Object that is an immutable class that all classes inherit from by default. The class java.lang.Object would not considered to be a part of the analyzed Scala project and would be treated as mutable without the assumption, effectively making all templates mu- table because they extend a mutable parent. Instead of considering all unknown templates mutable, a few assumptions about templates that are mutable are also included. These templates are selected with care, and include, for example, the mutable package of the standard library that are designed to have mutable templates. The assumptions that are part of the Scala standard library itself are not enabled when analyzing it, because these templates would not be unknown in the scenario of analyzing the library. For all projects except Scala’s standard library, we assume that classes in for example, scala.collection.immutable, are deeply immutable because the packages is designed to be immutable. The assumptions can be found in more-detail in the appendix A.2.

4.2 Evaluation

To answer the research questions of this degree project six different small-to-large open-source Scala projects are evaluted with the developed compiler plug-in. The following Scala projects and libraries are evaluated:

Scala’s standard library [22] The Scala standard library is included by default with the Scala programming language and consist of approximately 33107 lines of code (excluding blank lines and comments)1. The library have, for example, a math, I/O, concurrency packages (such as fu- tures) and an extensive collection package implemented in both a mutable and immutable version.

Akka’s actor [24] The standard actor implementation for Scala (16910 lines of code).

ScalaTest [25] A very popular testing framework for Scala (39452 lines of code).

Signal/Collect [26] A distributed graph processing framework based on Akka (10159 lines of code).

Scala-js [5] A Scala to JavaScript compiler (6500 lines of code).

Scala-fmt [27] A code formatter for Scala (6242 lines of code).

These projects were selected because they are popular, open sourced, do not depend on many third party libraries and are different in sizes (from smaller to larger). All projects, except for Scala’s stan- dard library, require third party libraries.

1Measured using cloc v 1.6 [23] Chapter 5

Results

This chapter presents the results of this degree project obtained with the methodology and evaluation detailed in chapter 4.

5.1 Projects Analyzed

Table 5.1 lists the six different Scala projects that we analyzed with the compiler plug-in and based our results on. A short description of each project can be found in section 4.2.

Scala project Project key Version Lines of code [23] Scala’s standard library standard-library 2.11.8 33107 Akka’s actor package akka-actor 2.4.17 16910 ScalaTest scala-test 3.0.1 39452 Signal/Collect signal-collect 8.0.2 10159 Scala-js scala-js 0.6.14 6500 Scalafmt scala-fmt 0.5.6 6242

Table 5.1: The Scala projects analyzed and the results of this chapter are based on.

The second column of table 5.1 is an assigned “project key” for each project that will from here on be used to refer to a project. For example, Akka’s actor package has the key akka-actor and will be referred to as akka-actor. Remember that projects make certain immutability assumptions about external dependencies as described in 4.1.4.

29 30 CHAPTER 5. RESULTS

5.2 Template Statistics

We begin with two figures, 5.1 and 5.2, that provide an overview of each analyzed project of table 5.1 showing the number of templates and the distribution of the different template types within each project.

Figure 5.1: The number of templates and the distribution of different types within the project for scala-fmt, signal-collect and scala-js.

Figure 5.2: The number of templates and the distribution of different types within the project for akka-aktor, standard-library and scala-test. CHAPTER 5. RESULTS 31

Table 5.2 provides the raw data of figure 5.1 and 5.2. It contains the number of analyzed templates for each project and template type, including how many of these templates that were unknown. If a template is unknown then, as explained in section 4.1.3, it means that we could not read the source of it and we need to assume that the template in turn has the mutable property.

Template scala-js scala-fmt akka-actor scala-test standard-library signal-collect Classes 61 (19,6%) 22 (10,4%) 299 (23,7%) 791 (32,4%) 626 (30,9%) 160 (53,2%) Case classes 92 (29,6%) 56 (26,4%) 206 (16,3%) 153 (6,3%) 75 (3,7%) 42 (14,0%) Anon classes 4 (1,3%) 28 (13,2%) 77 (6,1%) 688 (28,2%) 330 (16,3%) 4 (1,3%) Trait 16 (5,1%) 9 (4,2%) 239 (18,9%) 227 (9,3%) 466 (23,0%) 24 (8,0%) Object 77 (24,8%) 53 (25,0%) 220 (17,4%) 254 (10,4%) 358 (17,7%) 41 (13,6%) Case object 26 (8,4%) 27 (12,7%) 76 (6,0%) 81 (3,3%) 12 (0,6%) 5 (1,7%) Unknown 35 (11,3%) 17 (8,0%) 146 (11,6%) 246 (10,1%) 157 (7,8%) 25 (8,3%) Total 311 (100,0%) 212 (100,0%) 1263 (100,0%) 2440 (100,0%) 2024 (100,0%) 301 (100,0%)

Table 5.2: Gathered statistics of the different templates, including unknown, for each project analyzed.

Table 5.2 shows the gathered statistics about templates that were analyzed for the different projects. The number of templates ranges from 218 (scala-fmt) to 2786 (scala-test), where the seemingly most popular templates among the projects are classes and singleton objects. Only one project, signal-collect, have one kind of template with more that 30% occurrences and that are classes that stand for 53.2% of all templates. We also notice that the compiler project, scala-js, or the code formatter, scala-fmt, utilize more case classes than the other projects and we believe that this is due to the fact that these are easier to pattern match and pattern matching is a methodology that is natural in these type of projects. For the number of unknown templates akka-actor stands out with a high of 11.6% unknown tem- plates, meaning that all those templates were assumed to be mutable in the analysis. Scala’s standard li- brary, standard-library, had 7.8% unknown templates and was a little higher than expected for us. Our initial hypothesis was that it would be lower since it does not have any third-party dependencies like the other projects do. Manual inspection showed that a majority of unknown templates of standard-library were Java classes that the plug-in cannot read and analyze. 32 CHAPTER 5. RESULTS

5.3 Immutability Statistics

In this section we provide the results of analyzing the Scala projects listed in table 5.1 using the method- ology described in chapter 4. We begin with figure 5.3 that displays the distribution of immutability properties for each analyzed project based on table 5.3.

Figure 5.3: The distribution of the immutability properties for each analyzed project.

Project Mutable Shallow immutable Deeply immutable Conditionally deep scala-js 28 (10,1%) 131 (47,5%) 117 (42,4%) 0 (0%) scala-fmt 40 (20,5%) 58 (29,7%) 97 (49,7%) 0 (0%) akka-actor 204 (18,3%) 229 (20,5%) 586 (52,5%) 98 (8,8%) scala-test 513 (23,4%) 686 (31,3%) 924 (42,1%) 71 (3,2%) scala-compiler 891 (47,7%) 131 (7,0%) 553 (29,6%) 292 (15,6%) signal-collect 111 (40,2%) 41 (14,9%) 54 (19,6%) 70 (25,4%)

Table 5.3: Summarized immutability statistics for each analyzed project.

The following tables, table 5.4, 5.5, 5.6, 5.7, 5.8 and 5.9 are the immutability statistics for each project gathered from the implemented immutability analysis. The first column of each table indicates the analyzed Scala template and the second column the number of occurrences of each template type in the project. The following columns show the occurrences and percentage of each immutability prop- erty and template type. The last row is the sum of the rows above giving the total number of occur- rences for each template type and immutability property. CHAPTER 5. RESULTS 33

Template Occurrences Mutable Shallow Deep Conditionally Deep Class 61 (22,1%) 18 (29,5%) 24 (39,3%) 19 (31,1%) 0 (0%) Case class 92 (33,3%) 0 (0%) 84 (91,3%) 8 (8,7%) 0 (0%) Anonymous class 4 (1,4%) 2 (50,0%) 0 (0%) 2 (50,0%) 0 (0%) Trait 16 (5,8%) 4 (25,0%) 7 (43,8%) 5 (31,3%) 0 (0%) Object 77 (27,9%) 4 (5,2%) 16 (20,8%) 57 (74,0%) 0 (0%) Case object 26 (9,4%) 0 (0%) 0 (0%) 26 (100,0%) 0 (0%) Total 276 (100,0%) 28 (10,1%) 131 (47,5%) 117 (42,4%) 0 (0%)

Table 5.4: scala-js: Immutability statistics.

Template Occurrences Mutable Shallow Deep Conditionally Deep Class 22 (11,3%) 1 (4,5%) 9 (40,9%) 12 (54,5%) 0 (0%) Case class 56 (28,7%) 16 (28,6%) 23 (41,1%) 17 (30,4%) 0 (0%) Anonymous class 28 (14,4%) 23 (82,1%) 1 (3,6%) 4 (14,3%) 0 (0%) Trait 9 (4,6%) 0 (0%) 2 (22,2%) 7 (77,8%) 0 (0%) Object 53 (27,2%) 0 (0%) 17 (32,1%) 36 (67,9%) 0 (0%) Case object 27 (13,8%) 0 (0%) 6 (22,2%) 21 (77,8%) 0 (0%) Total 195 (100,0%) 40 (20,5%) 58 (29,7%) 97 (49,7%) 0 (0%)

Table 5.5: scala-fmt: Immutability statistics.

Template Occurrences Mutable Shallow Deep Conditionally Deep Class 299 (26,8%) 115 (38,5%) 93 (31,1%) 82 (27,4%) 9 (3,0%) Case class 206 (18,4%) 23 (11,2%) 64 (31,1%) 90 (43,7%) 29 (14,1%) Anonymous class 77 (6,9%) 33 (42,9%) 8 (10,4%) 36 (46,8%) 0 (0%) Trait 239 (21,4%) 22 (9,2%) 17 (7,1%) 140 (58,6%) 60 (25,1%) Object 220 (19,7%) 9 (4,1%) 47 (21,4%) 164 (74,5%) 0 (0%) Case object 76 (6,8%) 2 (2,6%) 0 (0%) 74 (97,4%) 0 (0%) Total 1117 (100,0%) 204 (18,3%) 229 (20,5%) 586 (52,5%) 98 (8,8%)

Table 5.6: akka-actor: Immutability statistics. 34 CHAPTER 5. RESULTS

Template Occurrences Mutable Shallow Deep Conditionally Deep Class 791 (36,1%) 216 (27,3%) 249 (31,5%) 288 (36,4%) 38 (4,8%) Case class 153 (7,0%) 15 (9,8%) 81 (52,9%) 54 (35,3%) 3 (2,0%) Anonymous class 688 (31,4%) 200 (29,1%) 293 (42,6%) 195 (28,3%) 0 (0%) Trait 227 (10,3%) 61 (26,9%) 45 (19,8%) 91 (40,1%) 30 (13,2%) Object 254 (11,6%) 19 (7,5%) 18 (7,1%) 217 (85,4%) 0 (0%) Case object 81 (3,7%) 2 (2,5%) 0 (0%) 79 (97,5%) 0 (0%) Total 2194 (100,0%) 513 (23,4%) 686 (31,3%) 924 (42,1%) 71 (3,2%)

Table 5.7: scala-test: Immutability statistics.

Template Occurrences Mutable Shallow Deep Conditionally Deep Class 626 (33,5%) 330 (52,7%) 54 (8,6%) 124 (19,8%) 118 (18,8%) Case class 75 (4,0%) 19 (25,3%) 7 (9,3%) 9 (12,0%) 40 (53,3%) Anonymous class 330 (17,7%) 209 (63,3%) 26 (7,9%) 95 (28,8%) 0 (0%) Trait 466 (25,0%) 224 (48,1%) 15 (3,2%) 93 (20,0%) 134 (28,8%) Object 358 (19,2%) 106 (29,6%) 29 (8,1%) 223 (62,3%) 0 (0%) Case object 12 (0,6%) 3 (25,0%) 0 (0%) 9 (75,0%) 0 (0%) Total 1867 (100,0%) 891 (47,7%) 131 (7,0%) 553 (29,6%) 292 (15,6%)

Table 5.8: standard-library: Immutability statistics.

Template Occurrences Mutable Shallow Deep Conditionally Deep Class 160 (58,0%) 78 (48,8%) 24 (15,0%) 14 (8,8%) 44 (27,5%) Case class 42 (15,2%) 4 (9,5%) 11 (26,2%) 15 (35,7%) 12 (28,6%) Anonymous class 4 (1,4%) 4 (100,0%) 0 (0%) 0 (0%) 0 (0%) Trait 24 (8,7%) 6 (25,0%) 1 (4,2%) 3 (12,5%) 14 (58,3%) Object 41 (14,9%) 19 (46,3%) 5 (12,2%) 17 (41,5%) 0 (0%) Case object 5 (1,8%) 0 (0%) 0 (0%) 5 (100,0%) 0 (0%) Total 276 (100,0%) 111 (40,2%) 41 (14,9%) 54 (19,6%) 70 (25,4%)

Table 5.9: signal-collect: Immutability statistics. CHAPTER 5. RESULTS 35

The most important insight given from the tables 5.4, 5.5, 5.6, 5.7, 5.9 and 5.8 is that a majority of templates of the projects do not have the mutable property and are instead either shallow immutable, deeply immutable or conditionally deeply immutable. This means that most templates satisfy one of im- mutability properties and a reasonable explanation could be that the functional programming style of Scala leads to higher immutability usage. To verify that claim one would have to do the same analy- sis and compare functional and none-functional programming languages. We can, however, say that at least half of the templates have an immutability property and there could be more because every project had an amount of unknown templates that we assume to be mutable (that could instead have an immutability property). More specifically, the two projects with the lowest number of templates that is mutable are scala-js with 10.1% (table 5.4) and akka-actor with 18.3% (table 5.6). The other projects all had more than 20% of their templates as mutable, scala-fmt with 20.5% (table 5.5), scala-test with 23.4% (table 5.7), signal-collect with 40.2% (table 5.9) and scala-standard-library with 47.7% (table 5.8). We can see that the lowest percentage 10.1% is significantly lower compared to the highest 47.7%. The fact that the standard library has a higher degree of mutable templates could be because of performance rea- sons. It uses mutability internally to optimize efficiency, something that is needed when it is widely used in many applications. Our analysis penalizes the mutability even if it could be abstract immutabil- ity and mutate in a none-observable manner for the user. Looking at the different templates types we see that their immutability property varies between the projects. The types case object and object are, however, throughout the projects more frequently deeply immutable, where all case objects were above 70% deeply immutable. An exception for object is the project signal-collect that had a high degree of mutable objects (46.3%). For projects that had tem- plates with the conditionally deeply immutable property, we see that this was the least common property and that the template type trait is most frequently used (the types object and anonymous class cannot have this property because they lack type parameters). 36 CHAPTER 5. RESULTS

5.4 Immutability Attributes

The previous section 5.3 showed gathered immutability statistics for each project. In this section we explore the reasons for the mutable and shallow immutable statistics. As described in chapter 4 we assign the deeply immutable property to a template until we find a reason not to, and if the template type has any type parameters, we assign it to be conditionally deeply immutable. The ten different im- mutability attributes listed in table 5.10 give a reason to why a template was given the immutability property mutable or shallow immutable. Each reason is associated with an “attribute” and the corre- sponding property.

Reason Immutability property Attribute Parent type mutable (assumption) mutable A Parent type mutable mutable B var field (public) mutable C var field (private) mutable Parent type unknown mutable E Parent type shallow immutable shallow immutable F val field refers to unknown type shallow immutable G val field refers to mutable type shallow immutable H val field refers to mutable type (assumption) shallow immutable I val field refers to shallow type shallow immutable J

Table 5.10: Reasons for the assigned immutability property immutable defined by an attribute.

If the immutability plug-in encounters one of the first five attributes (A to E) when analyzing a template it would assign the mutable property to it. For example, if the template had a parent that was mutable then attribute B would hold and be assigned the mutable property. The other attributes in the list are reasons for shallow immutable, for example, if a parent is shallow immutable (attribute F), then the analyzed template cannot be deeply immutable or conditionally deeply immutable. Each attribute is counted once per template type. For example, a class with multiple parents that are mutable (attribute B) would only count as one occurrence of having attribute B. If that template also has one or more var fields (attribute C) we count that template as having both attribute B and C. Figure 5.4, 5.5, 5.6, 5.7, 5.8 and 5.9 shows the distribution of each attribute for the larger projects akka-actor, scala-test and standard-library. All templates that have more than one attribute are clas- sified as “Combination” in the figures. For example, a template with attribute B and C, and another template with B and D in the same project are boh part of “Combination” in the figure. Immutabil- ity attributes for the projects signal-collect, scala-js and scala-fmt can be found in the appendix A.3 together with tables providing more detailed statistics for each project, all attribute combinations are omitted in the figures but are displayed in the appendix tables. CHAPTER 5. RESULTS 37

Figure 5.4: akka-actor: attributes causing Figure 5.5: akka-actor: attributes causing mutable. shallow.

Figure 5.7: scala-test: attributes causing Figure 5.6: scala-test: attributes causing shallow. mutable.

Figure 5.8: standard-library: attributes caus- Figure 5.9: standard-library: attributes caus- ing mutable. ing shallow. 38 CHAPTER 5. RESULTS

For the attributes that cause mutable, we see that “Parent type unknown” (attribute E) alone stands for 35.3% of the cases for akka-actor (figure 5.4), 53.6% for scala-test (figure 5.6) and 7.4% for standard- library (figure 5.8). For standard-library with the lowest rate of attribute E, the most common attribute is having a parent that is mutable (attribute B) that stand for 68.4% of the attributes causing mutable (figure 5.8). Attribute B is also frequent in both akka-actor and scala-test. This indicates that inheri- tance is an important factor when it comes to the immutability of a template. Another interesting and important property causing mutable is private reassignable fields (attribute D), which may indicate that these templates are abstract immutable and mutate in a none-visible manner. The most common reasons for shallow immutable are: the parent of the template were shallow im- mutable (attribute F), the existence of a non-reassignable field referring to a mutable (attribute H) or an unknown (attribute G) type. These attributes are more spread out within the projects compared to the attributes causing mutable where attribute E was dominant. Attribute F were most frequent (43.3%, figure 5.7) for scala-test, attribute G for akka-aktor (41.0%, figure 5.7) and attribute H (30.5%, figure 5.9) standard-library. CHAPTER 5. RESULTS 39

5.5 Conditional Deep Immutability

If a template type is deeply immutable and depends on a type parameter, we call it conditionally deeply immutable. The following section contains statistics on how mutable or shallow immutable templates are used with a template that is conditionally deeply immutable. We have identified two cases for where this can happen:

Case A: If a template extends a conditionally deeply immutable template and uses a mutable or shallow type as its type argument.

Case B: Same as case A, but instead of extending a template, it uses the template on a field with a mutable or shallow type as its type argument.

The following tables 5.11, 5.12, 5.13 and 5.14 show gathered occurrences of these two cases for the analyzed projects. The projects scala-js, scala-fmt and signal-collect had no occurrences of condi- tionally immutable templates being used this way. As we can see in tables 5.5 and 5.4, scala-fmt and scala-js did in fact not have a single template that was conditionally deeply immutable. Omitted tables, for example, case B for akka-actor means that there were none found and only case A was observed. 40 CHAPTER 5. RESULTS

Template Occurrences Mutable Shallow Class 6 (24,0%) 6 (100,0%) 0 (0%) Case class 11 (44,0%) 6 (54,5%) 5 (45,5%) Object 8 (32,0%) 0 (0%) 8 (100,0%) Total 25 (100,0%) 12 (48,0%) 13 (52,0%)

Table 5.11: akka-actor: case A

Template Occurrences Mutable Shallow Class 3 (50,0%) 1 (33,3%) 2 (66,7%) Anonymous class 3 (50,0%) 1 (33,3%) 2 (66,7%) Total 6 (100,0%) 2 (33,3%) 4 (66,7%)

Table 5.12: scala-test: case A

Template Occurrences Mutable Shallow Conditionally Deep Class 39 (19,9%) 36 (92,3%) 0 (0%) 3 (7,7%) Anonymous class 19 (9,7%) 17 (89,5%) 2 (10,5%) 0 (0%) Trait 53 (27,0%) 53 (100,0%) 0 (0%) 0 (0%) Object 85 (43,4%) 85 (100,0%) 0 (0%) 0 (0%) Total 196 (100,0%) 191 (97,4%) 2 (1,0%) 3 (1,5%)

Table 5.13: standard-library: case A

Template Occurrences Mutable Shallow Class 6 (40,0%) 5 (83,3%) 1 (16,7%) Case class 0 (0%) 0 (0%) 0 (0%) Anonymous class 7 (46,7%) 1 (14,3%) 6 (85,7%) Trait 1 (6,7%) 1 (100,0%) 0 (0%) Object 1 (6,7%) 0 (0%) 1 (100,0%) Total 15 (100,0%) 9 (60,0%) 6 (40,0%)

Table 5.14: standard-library: case B

Case A was apparent for three of the largest projects and perhaps surprisingly, only standard- library had occurrences of case A and B. In the results, we see that this behavior is most frequent for standard-library and there are very few occurrences for both akka-actor and scala-test. The standard library with 2024 templates had occurrences of case A in 9.68% of all templates. If it this was more prevalent then one could argue that developers need more help in knowing if a type is immutable or not, for example, by the type system. Chapter 6

Discussion

This chapter begins with a discussion and interpretation of the gathered results from chapter 5 in con- text of the project’s goal. Future work that is deemed relevant and limitations of the project are also discussed, followed by conclusions for the project’s research questions listed in section 1.2.

6.1 The Empirical Study

From the results gathered and listed in chapter 5, we can see that immutability statistics vary between the different projects analyzed. The aim of the empirical study and the gathered results was to try and find answers to the research questions listed in section 1.2. In the scope of this project, we believe that the most important results are present in order for the questions to be answered. However, besides the results given in chapter 5, we noticed that more data about immutability can be generated by our im- plemented tool. The number of unknown templates is surprisingly low for many of the projects and the initial hy- pothesis was that the dependencies unreachable from our analysis would be higher. The akka-actor project stands out with a high of 11.6% unknown templates, meaning that all those templates were as- sumed to be mutable in the analysis. Scala’s standard library, standard-library, had 7.8% unknown templates which was higher than expected and our theory was that it would be lower because it does not have any third-party dependencies like the other projects do. However, manual inspection showed that the majority of unknown templates of the standard-library were Java classes that the plug-in can- not read and analyze. The presence of unknown templates are unfortunate and it is hard to know ex- actly how much these affect the results. In a bad scenario, many templates would depend on one fre- quently used unknown template and these would all be classified as mutable or shallow immutable in- creasing these numbers. However, we think the results are interesting despite this fact and we can say that there can potentially be a lot more templates with the immutability properties. Furthermore, we found that the major reason for a template being mutable is “Parent type un- known” (attribute E). If we look at the distribution of reasons for mutable, using the data found in the appendix A.3, we find that attribute E stand for 8.6% of standard-library, 39.7% of akka-actor and 64.5% of scala-test, out of all mutable reasons. This make scala-test have the highest degree of mutable caused by unknown templates, but interestingly still only have 23.4% of all templates as mutable shown in table 5.7, consequently showing that the unknowns did not have a significant impact on immutability in this case. The results also indicate that inheritance is an important factor when it comes to the im- mutability of a template. This indication could, however, also be due to one frequently used unknown template (as previously mentioned). For example, if a commonly used templates has a parent that is unknown then this templates would become mutable and in return all templates that inherit from this

41 42 CHAPTER 6. DISCUSSION template would be become mutable. We cannot verify this with the data we have today, moreover, the same scenario could happen even if the template was not an unknown but actually mutable in the first place. Subsequently, a good reason for using deeply immutable data is that you know for a fact that once data is created, it cannot change. An interesting aspect is when deeply immutable data are mixed to- gether with mutable data. This can, of course, be by design, but it is also likely to be the cause of a mistake. Using deeply immutable data on, for example, a deeply immutable data structure, can lead to the wrong assumption that data in the structure cannot change. Because of the implementation of our analysis and that we assume unknown templates to be mutable we cannot be sure that the type argu- ment is in fact truly mutable. The analysis and the obtained results did unfortunately not consider if the type argument was mutable because of an unknown template. This behavior was nevertheless only found in three out of five projects and with very few occurrences. As this behavior rarely happened or did not occur at all for the smaller projects, it does not seem to be an issue.

6.2 Future Work

There are a lot of interesting topics to be explored in the area of immutability and some of which can be expanded upon the work presented in this project. The major issue with the type of analysis con- ducted in this project is the fact that all source code must be available, something that may not be the case for certain projects. A natural extension to our solution and this project would be to solve this is- sue. Combining both source code and bytecode analysis could, for example, be an interesting way of handling it. It would also be interesting to compare source code to bytecode analysis on a larger scale and see how much they differ in detecting immutability. The combination of dynamic and static analy- sis might also be an area worth exploring to yield improvements. Furthermore, the analyzed projects in this study are open-sourced, possibly developed by several people and considered popular, meaning that one can argue that they follow Scala conventions and best practices carefully. It would, therefore, be meaningful to analyze more projects, or other languages with a similar immutability features, to see if new insights can be found. To put immutability usage of real-world code into perspective, future work could be done in mea- suring immutability usage and, for example, the technical debt of programs in practice. Such as a study would give useful insights in many aspects since immutability is considered and said to ease reasoning about programs. For example, how cost-effective is to spend time and effort on ensuring immutability during the development of a program. Moreover, related to real-world code, it is clear that more research can be done into exploring how immutability is used in different languages. As we have seen in chapter 3, there are type systems that guarantee immutability, but not much data on how these are used or if they are used in practice. How does having built-in support for deep immutability affect the programmer, for example, what kind of compiler optimizations can be utilized or are there any useful documentation advantages? A final and important related area is to analyze abstract immutability which is lacking from the analysis provided in this project. Abstract immutability is an interesting concept, and we do not know how many of the shallow immutable or mutable templates are in fact abstract immutable or not in our analysis. There are many use cases of abstract immutability and supporting it in the analysis would most likely improve the results of it. CHAPTER 6. DISCUSSION 43

6.3 Conclusion

In this thesis, a novel immutability analysis of Scala source code is presented together with previously conducted immutability research. We evaluated templates of six small-to-large open-source Scala projects on three different immutability properties, shallow immutable, deeply immutable and condi- tionally deeply immutable all of which occurred frequently in our analysis. The analysis shows, even when assuming mutability to a large extent, that the majority of all templates for each project satis- fies an immutability property. Mutability was found to be lower for templates such as case classes and singleton objects, that can be seen as intended by the Scala designers, indicating that these type of ab- stractions help programmers with utilizing immutability. To summarize and answer our Research Questions (RQs): RQ1 How can a simple immutability analysis be defined and implemented in Scala? What are the needed immutability properties? We defined and implemented a simple immutability analysis and explained it in detail in chapter 4, the immutability properties are shallow immutable, deeply immutable and conditionally deeply immutable. RQ2 Using an implementation of the defined immutability analysis, how frequent is each immutability property for the different templates? The majority of templates of the projects are either shallow immutable, deeply immutable or con- ditionally deeply immutable, i.e., they were not mutable. Because every project had unreachable (unknown) templates that were assumed to be mutable, there could potentially be more templates that have an immutability property. The results varied between the projects and the template types trait and object were throughout the projects more frequently deeply immutable. RQ3 What are the reasons to why stronger immutability properties are not satisfied? From the results we conclude that inheritance is an important factor when it comes to the im- mutability of a template, both inheriting the mutable or shallow immutable property. Many tem- plates that were classified as mutable had a private reassignable field indicating that they may be abstract immutable and mutate in a none-observable manner, for example, used to cache im- mutable data. Moreover, a large percentage of the reasons for being shallow immutable is that none-reassignable fields refers to an unknown template meaning that the percentage of deeply immutable could be higher. RQ4 How often are types that are mutable or have a weaker immutability property used on templates whose immutability is conditional? We defined two cases of how this could happen, one case by inheritance and the other using fields, and the case of inheritance had a few occurrences only in three of the largest projects ana- lyzed. Only the Scala standard library had occurrences of both the first and second case, but only on a small percentage of the templates. We consider it a mistake to use a conditional template with a mutable or shallow immutable type, and because it rarely happened or did not occur at all for the smaller projects, this does not seem to be a widespread issue. These results are, however, unfortunately not very reliable because we assume that unknown templates are mutable. We believe that this type of research is valuable for programming language design, both for new and old languages and there is more future work and improvements that can be done. The insights gained from our results suggest that combining functional and imperative features in a language, such as Scala, leads to a high degree of immutability usage without much help from the language itself. Glossary

AST An abstract syntax tree (AST) is a tree representation of a program’s source code first.

JVM The Java Virtual Machine, or JVM runtime, that hosts a running Scala program..

RQ A research question this project will aim to answer.

template Following the terminology of the Scala language specification, a template refers to a class, case class, trait, object, or case object definition.

44 Bibliography

[1] Martin Odersky, Lex Spoon, and Bill Venners. Programming in Scala: Updated for Scala 2.12. Artima Incorporation, USA, 3rd edition, 2016. ISBN 0981531687, 9780981531687.

[2] Martin Odersky, Philippe Altherr, Vincent Cremet, Gilles Dubochet, Burak Emir, Philipp Haller, Stéphane Micheloud, Nikolay Mihaylov, Adriaan Moors, Lukas Rytz, Michel Schinz, Erik Stenman, and Matthias Zenger. The Scala language specification version 2.11. http: //www.scala-lang.org/files/archive/spec/2.11/, April 2014. URL http://www.scala-lang.org/files/archive/spec/2.11/.

[3] Alex Potanin, Johan Östlund, Yoav Zibin, and Michael D. Ernst. Immutability. In Alias- ing in Object-Oriented Programming. Types, Analysis and Verification, pages 233–269. 2013. doi: 10.1007/978-3-642-36946-9_9. URL http://dx.doi.org/10.1007/ 978-3-642-36946-9_9.

[4] Michael Coblenz, Joshua Sunshine, Jonathan Aldrich, Brad Myers, Sam Weber, and Forrest Shull. Exploring Language Support for Immutability. In Proceedings of the 38th Interna- tional Conference on Software Engineering, ICSE ’16, pages 736–747, New York, NY, USA, 2016. ACM. ISBN 978-1-4503-3900-1. doi: 10.1145/2884781.2884798. URL http: //doi.acm.org/10.1145/2884781.2884798.

[5] Scala.js - the Scala to JavaScript compiler. Version 0.6.14, May 2017. URL https://www. scala-js.org/.

[6] Denys Shabalin. Scala Native. An optimising ahead-of-time compiler for Scala built on top of the LLVM compiler infrastructure, May 2017. URL http://www.scala-native.org/.

[7] Benjamin C. Pierce. Types and Programming Languages. The MIT Press, 1st edition, 2002. ISBN 0262162091, 9780262162098.

[8] Unified Types. May 2017. URL http://docs.scala-lang.org/tutorials/tour/ unified-types.html.

[9] J. Hughes. Why Functional Programming Matters. Comput. J., 32(2):98–107, April 1989. ISSN 0010-4620. doi: 10.1093/comjnl/32.2.98. URL http://dx.doi.org/10.1093/ comjnl/32.2.98.

[10] Joshua Bloch. Effective Java (2Nd Edition) (The Java Series). Prentice Hall PTR, Upper Saddle River, NJ, USA, 2 edition, 2008. ISBN 0321356683, 9780321356680.

[11] Adrian Birka and Michael D. Ernst. A Practical Type System and Language for Reference Im- mutability. SIGPLAN Not., 39(10):35–49, October 2004. ISSN 0362-1340. doi: 10.1145/ 1035292.1028980. URL http://doi.acm.org/10.1145/1035292.1028980.

45 46 BIBLIOGRAPHY

[12] Tim Sheard. Accomplishments and Research Challenges in Meta-programming, pages 2–44. Springer Berlin Heidelberg, Berlin, Heidelberg, 2001. ISBN 978-3-540-44806-8. doi: 10.1007/ 3-540-44806-3_2. URL http://dx.doi.org/10.1007/3-540-44806-3_2.

[13] Jaime Quinonez, Matthew S. Tschantz, and Michael D. Ernst. Inference of Reference Im- mutability. In Proceedings of the 22Nd European Conference on Object-Oriented Programming, ECOOP ’08, pages 616–641, Berlin, Heidelberg, 2008. Springer-Verlag. ISBN 978-3-540- 70591-8. doi: 10.1007/978-3-540-70592-5_26. URL http://dx.doi.org/10.1007/ 978-3-540-70592-5_26.

[14] Yoav Zibin, Alex Potanin, Mahmood Ali, Shay Artzi, Adam Kie, un, and Michael D. Ernst. Object and Reference Immutability Using Java Generics. In Proceedings of the the 6th Joint Meeting of the European Software Engineering Conference and the ACM SIGSOFT Symposium on The Foundations of Software Engineering, ESEC-FSE ’07, pages 75–84, New York, NY, USA, 2007. ACM. ISBN 978-1-59593-811-4. doi: 10.1145/1287624.1287637. URL http://doi.acm.org/10.1145/1287624.1287637.

[15] Colin S. Gordon, Matthew J. Parkinson, Jared Parsons, Aleks Bromfield, and Joe Duffy. Unique- ness and Reference Immutability for Safe Parallelism. SIGPLAN Not., 47(10):21–40, October 2012. ISSN 0362-1340. doi: 10.1145/2398857.2384619. URL http://doi.acm.org/ 10.1145/2398857.2384619.

[16] Igor Pechtchanski and Vivek Sarkar. Immutability Specification and Its Applications. In Pro- ceedings of the 2002 Joint ACM-ISCOPE Conference on Java Grande, JGI ’02, pages 202–211, New York, NY, USA, 2002. ACM. ISBN 1-58113-599-8. doi: 10.1145/583810.583833. URL http://doi.acm.org/10.1145/583810.583833.

[17] Michael Eichberg and Ben Hermann. A Software Product Line for Static Analyses: The OPAL Framework. In Proceedings of the 3rd ACM SIGPLAN International Workshop on the State of the Art in Java Program Analysis, SOAP ’14, pages 1–6, New York, NY, USA, 2014. ACM. ISBN 978-1-4503-2919-4. doi: 10.1145/2614628.2614630. URL http://doi.acm.org/10. 1145/2614628.2614630.

[18] Jon Eyolfson and Patrick Lam. C++ const and Immutability: An Empirical Study of Writes- Through-const. In Shriram Krishnamurthi and Benjamin S. Lerner, editors, 30th European Conference on Object-Oriented Programming (ECOOP 2016), volume 56 of Leibniz International Proceedings in Informatics (LIPIcs), pages 8:1–8:25, Dagstuhl, Germany, 2016. Schloss Dagstuhl– Leibniz-Zentrum fuer Informatik. ISBN 978-3-95977-014-9. doi: http://dx.doi.org/10.4230/ LIPIcs.ECOOP.2016.8. URL http://drops.dagstuhl.de/opus/volltexte/ 2016/6102.

[19] Fredrik Kjolstad, Danny Dig, Gabriel Acevedo, and Marc Snir. Transformation for Class Im- mutability. In Proceedings of the 33rd International Conference on Software Engineering, ICSE ’11, pages 61–70, New York, NY, USA, 2011. ACM. ISBN 978-1-4503-0445-0. doi: 10.1145/ 1985793.1985803. URL http://doi.acm.org/10.1145/1985793.1985803.

[20] Graham Allan. Mutability Detector. May 2017. URL http://mutabilitydetector. org/. BIBLIOGRAPHY 47

[21] Philipp Haller, Simon Geries, Michael Eichberg, and Guido Salvaneschi. Reactive Async: Ex- pressive Deterministic Concurrency. In Proceedings of the 2016 7th ACM SIGPLAN Sympo- sium on Scala, SCALA 2016, pages 11–20, New York, NY, USA, 2016. ACM. ISBN 978-1- 4503-4648-1. doi: 10.1145/2998392.2998396. URL http://doi.acm.org/10.1145/ 2998392.2998396.

[22] Scala’s standard library. Version 2.11.8, May 2017. URL http://www.scala-lang.org/ api/2.11.8.

[23] AlDanial. Cloc: Count Lines of Code. Version 1.6, May 2017. URL https://github. com/AlDanial/cloc.

[24] Akka’s actor - the standard Actor implementation for Scala. Version 2.4.17, May 2017. URL http://akka.io/.

[25] ScalaTest - A testing framework for Scala. Version 3.0.1, May 2017. URL http://www. scalatest.org/.

[26] Signal/collect - a distributed graph processing framework based on Akka. Version 8.0.2, May 2017. URL http://uzh.github.io/signal-collect/.

[27] Ólafur Páll Geirsson. Scala-fmt - a code formatter for scala. Version 0.5.6, May 2017. URL http://www.scalafmt.org. Appendix A

Source Code and Data

A.1 The Analysis Program

The analysis and compiler plug-in developed for this project can be found at:

https://github.com/luax/scala-immutability-plugin

Instructions on how to use the analysis and reproduce the results can also be found there.

A.2 Immutability Assumptions

The different immutability assumptions can be found in the project file Assumptions.scala at the path /plugin/src/main/scala/immutability/Assumptions.scala, or by following this link:

https://github.com/luax/scala-immutability-plugin/blob/ 88ef151fd82572ae7cc16022a0db57a3d93599ff/plugin/src/main/scala/ immutability/Assumptions.scalaa

Some of the assumptions for deeply immutable that were used on all projects are, for example:

• java.io.Serializable • scala.Any • scala.Long

• java.lang.Class • scala.AnyVal • scala.None

• java.lang.Comparable • scala.Boolean • scala.Nothing

• java.lang.Error • scala.Byte • scala.Null

• java.lang.Exception • scala.Char • scala.Option

• java.lang.Object • scala.Double • scala.Product

• java.lang.String • scala.Float • scala.Serializable

• java.lang.Throwable • scala.Int • scala.Some

48 APPENDIX A. SOURCE CODE AND DATA 49

We also assumed these classes to be mutable when not analyzing the standard library project:

• scala.collection.BufferedIterator • scala.collection.Traversable • scala.collection.IndexedSeq • scala.collection.TraversableOnce • scala.collection.Iterable • scala.collection.mutable.* • scala.collection.Iterator • scala.collection.Seq • scala.collection.parallel.mutable.*

The classes that were in fact used in the analysis of the mutable package were:

• scala.collection.mutable.ArrayBuffer

• scala.collection.mutable.Builder

• scala.collection.mutable.ListBuffer

• scala.collection.mutable.Map

• scala.collection.mutable.Set

• scala.collection.mutable.SynchronizedBuffer

• scala.collection.parallel.mutable.ParArray

The assumptions starting with the word scala are templates of Scala and those starting with java are Java classes. 50 APPENDIX A. SOURCE CODE AND DATA

A.3 Immutability Attributes

Reason Immutability property Attribute Parent type mutable (assumption) mutable A Parent type mutable mutable B var field (public) mutable C var field (private) mutable D Parent type unknown mutable E Parent type shallow immutable shallow immutable F val field refers to unknown type shallow immutable G val field refers to mutable type shallow immutable H val field refers to mutable type (assumption) shallow immutable I val field refers to shallow type shallow immutable J

Table A.1: Reasons for the assigned immutability property immutable defined by an attribute.

Table A.2, A.3, A.4, A.5, A.6 and A.7 indicate how many occurrences of each attribute per project was found. The sum of all occurrences are therefore the total number of mutable or shallow immutable template types found in the appropriate table of section 5.3, for example, table 5.8 for the Scala stan- dard library. APPENDIX A. SOURCE CODE AND DATA 51

Attribute(s) Occurrences Attribute(s) Occurrences A 3 (1,5%) F 38 (16,6%) ABD 1 (0,5%) FG 9 (3,9%) AE 1 (0,5%) FGH 2 (0,9%) B 76 (37,3%) FGJ 3 (1,3%) BC 3 (1,5%) FH 3 (1,3%) BCD 1 (0,5%) FJ 3 (1,3%) BD 6 (2,9%) G 94 (41,0%) BE 6 (2,9%) GH 8 (3,5%) C 7 (3,4%) GHI 1 (0,4%) CD 2 (1,0%) GHJ 1 (0,4%) CDE 1 (0,5%) GJ 16 (7,0%) D 24 (11,8%) H 22 (9,6%) DE 1 (0,5%) HJ 4 (1,7%) E 72 (35,3%) J 25 (10,9%)

Table A.2: akka-actor: attributes causing Table A.3: akka-actor: attributes causing mutable. shallow immutable.

Attribute(s) Occurrences F 297 (43,3%) FG 10 (1,5%) FGH 1 (0,1%) FGHI 1 (0,1%) FGHIJ 2 (0,3%) FGJ 10 (1,5%) FH 2 (0,3%) FHJ 15 (2,2%) Attribute(s) Occurrences FJ 3 (0,4%) A 10 (1,9%) G 198 (28,9%) ABE 22 (4,3%) GH 2 (0,3%) AD 1 (0,2%) GHI 1 (0,1%) B 120 (23,4%) GHJ 6 (0,9%) BD 3 (0,6%) GI 14 (2,0%) BDE 1 (0,2%) GJ 2 (0,3%) BE 26 (5,1%) H 20 (2,9%) C 13 (2,5%) HI 1 (0,1%) CE 1 (0,2%) HJ 21 (3,1%) D 35 (6,8%) I 43 (6,3%) DE 6 (1,2%) IJ 5 (0,7%) E 275 (53,6%) J 32 (4,7%)

Table A.4: scala-test: attributes causing Table A.5: scala-test: attributes causing mutable. shallow immutable. 52 APPENDIX A. SOURCE CODE AND DATA

Attribute(s) Occurrences F 28 (21,4%) Attribute(s) Occurrences FG 5 (3,8%) B 609 (68,4%) FGJ 1 (0,8%) BC 71 (8,0%) FH 4 (3,1%) BCD 1 (0,1%) FJ 6 (4,6%) BD 19 (2,1%) G 21 (16,0%) BE 7 (0,8%) GH 8 (6,1%) C 26 (2,9%) GHJ 2 (1,5%) CD 1 (0,1%) GJ 3 (2,3%) D 87 (9,8%) H 43 (32,8%) DE 4 (0,4%) HJ 3 (2,3%) E 66 (7,4%) J 7 (5,3%)

Table A.6: standard-library: attributes caus- Table A.7: standard-library: attributes caus- ing mutable. ing shallow immutable.

Attribute(s) Occurrences A 5 (4,5%) AC 3 (2,7%) Attribute(s) Occurrences B 25 (22,5%) F 2 (4,9%) BC 6 (5,4%) FH 1 (2,4%) BCD 1 (0,9%) G 18 (43,9%) BCDE 1 (0,9%) GH 2 (4,9%) BD 2 (1,8%) GHIJ 1 (2,4%) C 26 (23,4%) GHJ 1 (2,4%) CD 3 (2,7%) GI 1 (2,4%) CDE 1 (0,9%) GJ 2 (4,9%) CE 5 (4,5%) H 8 (19,5%) D 3 (2,7%) HJ 1 (2,4%) E 30 (27,0%) J 4 (9,8%)

Table A.8: signal-collect: attributes causing Table A.9: signal-collect: attributes causing shallow immutable. shallow immutable. APPENDIX A. SOURCE CODE AND DATA 53

Attribute(s) Occurrences F 8 (13,8%) FG 1 (1,7%) FJ 1 (1,7%) G 19 (32,8%) GH 2 (3,4%) GHI 1 (1,7%) GHIJ 1 (1,7%) GHJ 1 (1,7%) GI 2 (3,4%) Attribute(s) Occurrences GJ 5 (8,6%) BE 16 (40,0%) H 5 (8,6%) C 1 (2,5%) IJ 2 (3,4%) E 23 (57,5%) J 10 (17,2%)

Table A.10: scala-fmt: attributes causing Table A.11: scala-fmt: attributes causing mutable. shallow immutable.

Attribute(s) Occurrences F 3 (2,3%) FG 1 (0,8%) Attribute(s) Occurrences G 39 (29,8%) A 1 (3,6%) GH 1 (0,8%) B 5 (17,9%) GHI 1 (0,8%) C 2 (7,1%) GI 2 (1,5%) D 4 (14,3%) H 4 (3,1%) DE 2 (7,1%) I 1 (0,8%) E 14 (50,0%) J 79 (60,3%)

Table A.12: scala-js: attributes causing muta- Table A.13: scala-js: attributes causing shal- ble. low immutable. www.kth.se