Skip to content

Instantly share code, notes, and snippets.

@psygo
Last active February 12, 2021 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save psygo/6ee55f7c744646b39a306c4645cd43ed to your computer and use it in GitHub Desktop.
Save psygo/6ee55f7c744646b39a306c4645cd43ed to your computer and use it in GitHub Desktop.
FIC: Fast Immutable Collections Motivation Demonstration
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:meta/meta.dart";
//------------------------------------------------------------------------------
void main() {
//----------------------------------------------------------------------------
print("With Regular Collections\n\n");
const bob = Person("Bob");
var people1 = People([bob]);
print(people1);
const jake = Person("Jake");
var people2 = people1.add(jake);
print(people1);
print(people2);
print(people1 == people2); // false
print(people2 == People([bob, jake])); // true
print(people1.hashCode == people2.hashCode); // false
print(people2.hashCode == People([bob, jake]).hashCode); // true
//----------------------------------------------------------------------------
print("\n\n");
//----------------------------------------------------------------------------
print("With FIC\n\n");
var peopleWithFic1 = PeopleWithFic([bob].lock);
var peopleWithFic2 = peopleWithFic1.add(jake);
print(peopleWithFic1);
print(peopleWithFic2);
print(peopleWithFic1 == peopleWithFic2); // false
print(peopleWithFic2 == PeopleWithFic([bob, jake].lock)); // true
print(peopleWithFic1.hashCode == peopleWithFic2.hashCode); // false
print(peopleWithFic2.hashCode == PeopleWithFic([bob, jake].lock).hashCode); // true
//----------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
@immutable
class PeopleWithFic {
final IList<Person> people;
PeopleWithFic(this.people);
PeopleWithFic add(Person person) => PeopleWithFic(people.add(person));
@override
bool operator ==(Object other) => identical(this, other) ||
other is PeopleWithFic &&
runtimeType == other.runtimeType &&
people == other.people;
@override
int get hashCode => people.hashCode;
@override
String toString() => people.toString();
}
//------------------------------------------------------------------------------
@immutable
class People {
final List<Person> _people;
People(List<Person> people): _people = List.of(people);
People._(this._people);
// An example "mutable" operaton. Now imagine dozens of these operations,
// each needing extra care from you in order to not cause any side effects.
People add(Person person) {
var newPeopleList = List.of(_people)..add(person);
return People._(newPeopleList);
}
// If you want to create value equality for this class, you will need to
// override reference equality. The simplest way is to do this is to use the
// collection package.
@override
bool operator ==(Object other) => identical(this, other) ||
other is People &&
runtimeType == other.runtimeType &&
const ListEquality().equals(_people, other._people);
@override
int get hashCode => const ListEquality().hash(_people);
@override
String toString() => _people.toString();
// You could also write your own list equality. Something close to this:
bool _listEquals<T>(List<T> a, List<T> b) {
if (a == null)
return b == null;
if (b == null || a.length != b.length)
return false;
if (identical(a, b))
return true;
// Don't write this recursively, you might end up with space leaks.
// For more info on this, check out Tail Call Optimization (TCO).
for (int index = 0; index < a.length; index += 1) {
if (a[index] != b[index]) return false;
}
return true;
}
}
//------------------------------------------------------------------------------
@immutable
class Person {
final String name;
const Person(this.name);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
@override
String toString() => "Person: $name";
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
import "package:fast_immutable_collections/fast_immutable_collections.dart";
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void main() {
//----------------------------------------------------------------------------
print("\n\n------------------------------------------------------------\n\n");
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// 1) This is ok. Immutability does work in this case.
// (This is covered in the `List.unmodifiable` docs.)
print("1) Direct Immutability\n\n");
var list = [1, 2, 3];
var unmodifiableList1 = List.unmodifiable(list);
list.add(4);
print("Original List: $list");
print("Unmodifiable List 1: $unmodifiableList1");
//----------------------------------------------------------------------------
print("\n\n------------------------------------------------------------\n\n");
//----------------------------------------------------------------------------
// 2) This is not ok. Immutability doens't work because the nested elements
// are not themselves immutable.
// This is also covered in the `List.unmodifiable` docs.)
print("2) Nested Immutability\n\n");
var list1 = [1, 2];
var list2 = [1, 2, 3];
var nestedList = [list1, list2];
var unmodifiableList2 = List.unmodifiable(nestedList);
list1.add(10);
print("Original List 1: $list1");
print("Original List 2: $list2");
print("Unmodifiable List 2: $unmodifiableList2");
//----------------------------------------------------------------------------
print("\n\n------------------------------------------------------------\n\n");
//----------------------------------------------------------------------------
// 3) How would you modify, add, remove, etc. this `List.unmodifiable` object?
// FIC makes it much simpler and less error prone...
// And copying things to make changes will make `List.unmodifiable`
// unbearably slow.
print("3) Clunky modifications natively | FIC makes it easy!\n\n");
var list3 = [1, 2, 3];
var unmodifiableList3 = List.unmodifiable(list3);
var ilist3 = list3.lock;
var newUnmodifiableList3 =
List.unmodifiable(unmodifiableList3.toList()..add(4));
var newIlist3 = ilist3.add(4);
print("New UnmodifiableList 3: $newUnmodifiableList3");
print("New IList 3: $newIlist3");
//----------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
name: fic_example
author: Philippe Fanaro <philippefanaro@gmail.com
dependencies:
fast_immutable_collections: ^1.0.19
collection: ^1.14.13
meta: ^1.2.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment