Skip to content

Commit 420b991

Browse files
committed
Propose specification.
1 parent edb1be6 commit 420b991

File tree

2 files changed

+226
-1
lines changed

2 files changed

+226
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# kotlinx.collections.immutable
22

3-
Immutable collection interfaces and implementation prototypes for Kotlin.
3+
Immutable collection interfaces and implementation prototypes for Kotlin ([proposal](proposal.md))
4+
45

56
To initialize submodules, use the following commands:
67

proposal.md

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Immutable Collections
2+
3+
* **Type**: Standard/kotlinx Library API proposal
4+
* **Author**: Ilya Gorbunov
5+
* **Status**: Submitted
6+
* **Prototype**: In progress
7+
* **Discussion**: In the [issues](https://github.com/Kotlin/kotlinx.collections.immutable/issues) of this repository
8+
9+
## Summary
10+
11+
Provide interfaces to represent truly immutable collections and maps.
12+
13+
Provide implementation of those interfaces.
14+
15+
## Similar API review
16+
17+
* [.NET immutable collections](https://msdn.microsoft.com/en-us/library/mt452182.aspx)
18+
* [Scala immutable collections](http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes)
19+
20+
## Description
21+
22+
There are two families of collection interfaces in the Standard Library:
23+
24+
* read-only interfaces, which only allow to read elements from a collection;
25+
* mutable interfaces, which additionally allows to modify elements in a collection.
26+
27+
Kotlin mostly doesn't provide its own collection implementations.
28+
Its collection interfaces are mapped to the standard JDK collection interfaces
29+
and implemented by the JDK collection implementations, such as `ArrayList`, `HashSet`, `HashMap` and other
30+
classes from `java.util` package.
31+
32+
It is proposed to provide immutable collection interfaces which extend read-only interfaces
33+
and specify by the contract the real immutability of their implementors.
34+
35+
### Modification operations
36+
37+
Immutable collections could have same modification operations as their mutable counterparts, such as `add(element)`,
38+
`remove(element)`, `put(key, value)` etc., but unlike mutating operations they do not mutate the receiver, but instead
39+
return new immutable collection instance with the modification applied.
40+
41+
In case if no modification is made during the operation, for example, adding an existing element to a set or clearing
42+
already empty collection, it should return the same instance of the immutable collection.
43+
44+
### Builders
45+
46+
Often there is a need to perform several subsequent modification operations on an immutable collection. It can be done by
47+
- chaining operations together:
48+
```
49+
collection = collection.add(element).add(anotherElement)
50+
```
51+
- applying operations one by one:
52+
```
53+
collection += element
54+
collection += anotherElement
55+
```
56+
- using collection builders.
57+
58+
An immutable collection builder is a wrapper around an immutable collection, which exposes its modification operations
59+
within the mutable collection interface. For example, `ImmutableSet.Builder<T>` extends `MutableSet<T>`.
60+
61+
An immutable collection could be transformed to a builder with `builder()` method, and a builder could be
62+
transformed back to the immutable collection with its `build()` method.
63+
64+
```
65+
val builder = immutableList.builder()
66+
builder.removeAll { it.value > treshold }
67+
if (builder.isEmpty()) {
68+
builder.add(defaultValue)
69+
}
70+
return builder.build()
71+
```
72+
73+
In case if each operation invoked on a builder causes no modifications,
74+
the collection returned by `build()` method is the same collection the builder was obtained from.
75+
76+
Builders have two advantages:
77+
- Allow to utilize existing API taking mutable collections.
78+
- Builders could keep their internal data structures mutable and finalize them only when `build()` method is called.
79+
80+
### Variance
81+
82+
It's convenient for immutable collections to have the same variance as the read-only collections they extend.
83+
However their modification operations could have covariant type appear in parameter (contravariant) position,
84+
therefore that parameter type must be annotated with `@UnsafeVariance` annotation, and special care must be given when
85+
implementing such methods.
86+
87+
### Interfaces and implementations
88+
89+
#### ImmutableCollection
90+
91+
- Extends read-only Collection.
92+
- Provides builder as a mutable collection.
93+
94+
#### ImmutableList
95+
96+
- Extends read-only List and immutable Collection.
97+
- Provides builder as a mutable list.
98+
- Has the single implementation, returned by `immutableListOf` function.
99+
100+
#### ImmutableSet
101+
102+
- Extends read-only Set and immutable Collection.
103+
- Provides builder as a mutable set.
104+
- Has two implementations (`immutableSetOf`, `immutableHashSetOf`)
105+
differing by whether the insertion order of elements is preserved.
106+
107+
#### ImmutableMap
108+
109+
- Extends read-only Map.
110+
- Provides builder as a mutable map.
111+
- Has two implementations (`immutableMapOf`, `immutableHashMapOf`)
112+
differing by whether the insertion order of keys is preserved.
113+
114+
### Extension functions
115+
116+
#### toImmutableList/Set/Map
117+
Converts read-only or mutable collection to immutable one.
118+
If the receiver is already immutable and has the required type, returns itself.
119+
120+
fun Iterable<T>.toImmutableList(): ImmutableList<T>
121+
fun Iterable<T>.toImmutableSet(): ImmutableSet<T>
122+
123+
#### `+` and `-` operators
124+
125+
`plus` and `minus` operators on immutable collections can exploit their immutability
126+
and delegate the implementation to the collections themselves.
127+
128+
#### mutate
129+
130+
A quite common pattern with builders arises: get a builder, apply some mutating operations on it,
131+
transform it back to an immutable collection:
132+
133+
collection.builder().apply { some_actions_on(this) }.build()
134+
135+
This pattern could be extracted to an extension function.
136+
137+
```
138+
ImmutableList.mutate(action: (MutableList) -> Unit) =
139+
builder().apply { action(this) }.build()
140+
```
141+
142+
## Use cases
143+
144+
* Avoiding defensive copying of collection passed as a parameter
145+
when you need an unchangeable snapshot and you're unsure whether the collection could be changed later.
146+
147+
```
148+
class Query(val parameters: ImmutableList<Parameter>)
149+
150+
// or
151+
152+
class Query(parameters: List<Parameter>) {
153+
// creates an immutable list or does nothing if it's already immutable
154+
val parameters = parameters.toImmutable()
155+
}
156+
```
157+
158+
* Sharing a single immutable collection between multiple threads. Since the collection
159+
cannot be changed neither by producer, nor by any consumer, you'll never get a `ConcurrentModificationException`
160+
during iteration, or any other symptoms of corrupted state.
161+
162+
* Having multiple snapshots of a collection sharing their element storage.
163+
164+
## Alternatives
165+
166+
* Create a mutable collection, but expose it only as its read-only interface.
167+
This pattern is mostly employed in the standard library collection operations.
168+
169+
* Pro: mutable collection implementations are generally more memory efficient,
170+
require less allocations during and after their populating.
171+
* Con: collection can be cast back to mutable (e.g. involving platform types),
172+
and then unexpectedly mutated.
173+
* Con: different snapshots of a collection do not share any element storage.
174+
* Con: defensive copying of the resulting collection is still required later
175+
in a chain of operations.
176+
177+
178+
## Dependencies
179+
180+
What are the dependencies of the proposed API:
181+
182+
* interfaces depend only on a subset of Kotlin Standard Library available on all supported platforms.
183+
* current collection implementations are based on [PCollections](http://pcollections.org) library,
184+
which is being shaded into the resulting artifact. This results in dependency on JVM 1.6 and upper.
185+
186+
## Placement
187+
188+
* Package: `kotlin.collections.immutable`
189+
* At first the API is to be provided in a separate module `kotlin-collections-immutable`,
190+
but later it may be merged into `kotlin-stdlib`.
191+
192+
## Reference implementation
193+
194+
Reference implementation is given in this repository, see the
195+
[source](https://github.com/ilya-g/kotlinx.collections.immutable/tree/master/kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable).
196+
197+
## Known issues
198+
199+
1. There is no implementation of immutable ordered map.
200+
2. Immutable collections currently aren't `Serializable`.
201+
202+
## Unresolved questions
203+
204+
1. Should we expose immutable collection implementations to the public API? Current experience shows
205+
that it's not mandatory.
206+
2. Immutable map provides `keys`, `entries` sets and `values` collection.
207+
Should these collections be also immutable, or just read-only is enough?
208+
Note that their implementations are not persistent and most of their
209+
modification operations require to make a copy of the collection.
210+
3. `Map - key` operation: we do not support such operation for read-only maps, and for mutable maps we
211+
can do `MutableMap.keys -= key`. What would be the analogous operation for `ImmutableMap`?
212+
4. `mutate` extension: should the action take `MutableList` as a receiver
213+
or as a parameter (`this` or `it` inside lambda)?
214+
5. `immutableMapOf<K,V>().builder()` requires explicit specification of K and V type arguments
215+
(until the common type inference system is implemented) and is quite lengthy.
216+
Should we provide some shortcut to infer types from expected map type?
217+
218+
219+
## Future advancements
220+
221+
* Provide other immutable collection interfaces such as `ImmutableStack` and `ImmutableQueue`
222+
and their implementations.
223+
* Improve standard library collection operations to exploit benefits of immutability of the source collections.
224+

0 commit comments

Comments
 (0)