Trve immutable wrapper classes for Dart collections.
Add immortal
as dependency in your pubspec.yaml
:
dependencies:
...
immortal: ^3.0.0
Import immortal
:
import 'package:immortal/immortal.dart';
-
Lists
final immortalList = ImmortalList([1, 2, 3]); final modifiedList = immortalList .add(4) .where((value) => value.isEven) .map((value) => value * 2); print(immortalList); // prints "Immortal[1, 2, 3]" print(modifiedList); // prints "Immortal[4, 8]"
-
Sets
final immortalSet = ImmortalSet({1, 2, 3}); final modifiedSet = immortalSet .union(ImmortalSet({1, 2})) .remove(1); print(immortalSet); // prints "Immortal{1, 2, 3}" print(modifiedSet); // prints "Immortal{2, 3}"
-
Maps
final immortalMap = ImmortalMap({1: 'a', 2: 'b', 3: 'c'}); final modifiedMap = immortalMap .putIfAbsent(2, () => 'e') .update(1, (value) => value.toUpperCase()); print(immortalMap); // prints "Immortal{1: 'a', 2: 'b', 3: 'c'}" print(modifiedMap); // prints "Immortal{1: 'A', 2: 'b', 3: 'c'}"
For a complete list of all functions defined on the immortal
collections see the API reference.
This library provides a more functional wrapper around the dart:core
collections List
, Set
and Map
called ImmortalList
, ImmortalSet
and ImmortalMap
.
It is designed with usability and readability in mind while staying close to the method names of their mutable counterparts.
This should make switching from mutable to Immortal
collections in existing applications fairly easy.
In order to reach complete immutability, the elements have to be immutable as well.
If you want to go fully functional, however, you should consider using e.g. dartz
instead.
The key objectives for this library are the following:
- Mutation methods return new instances instead of throwing exceptions
- No builder pattern to create new instances
- Optionals for nullable return values or at least providing alternatives using optionals (e.g. for list methods required to implement the
Iterable
interface likefirst
there is an alternativefirstAsOptional
) - High fault tolerance in general, e.g. list indices passed as parameters are adjusted to fit inside the list's boundaries (except for list methods required to implement the
Iterable
interface, but alternatives using optionals are provided as stated above) - Return the same instance if no changes were made in mutation methods (there is still some potential to improve and extend this in future updates of this library)
- No assumptions about the order of elements in a set:
ImmortalSet
does not implement theIterable
interface and provides methods likeelementAt
,first
,skip
etc. even though the underlying implementation of Dart'sSet
might do so. - Provide more common names in addition to the Dart method names, like
flatMap
forexpand
(this is something that might be extended in future updates as well) - Addition of useful methods missing in
dart:core
, e.g.mapKeys
andmapValues
onImmortalMap
(this is again something that might be extended in future library updates). - Encourage further usage of immutable collections by prefering
Immortal
parameters. - Encourage comparison by identity only by not overriding the
==
operator. To compare collections by their contentequals
methods are provided. - Designed to write elegant code - not neccessarily the most performant. But immutability usually comes with a price anyways 🤷 (this might still be improved in future versions of this library)
Yet another implementation of immutable collections for Dart..?
I was never fully satisfied with other libraries that provide immutable collections for Dart. They either do not provide mutation methods to create new, modified instances or only do so by the usage of the builder pattern. This way they are often cumbersome to use and I felt I had to write too much code in order to perform simple tasks such as adding or updating a single element.
So I decided to write my own.
The main purpose of use for me is inside Redux or other component states in Flutter applications. I want to write short and simple reducer functions while being able to check for identity alone when I want to find out if the state has changed. As checks for state changes usually happen a lot more frequently (to find out if a component has to be rebuilt) as actual changes to the state, the performance of creating and updating instances was not my main focus while writing this library.
I wanted to prevent name clashes with other libraries - and I just think the name is fitting and cool 🤘
In eternity and time the same still the tundra lay untouched