Skip to content

Commit

Permalink
Merge pull request #76 from LowLevelSubmarine/feat-ilist-empty-type
Browse files Browse the repository at this point in the history
feat: IListEmpty and friends
  • Loading branch information
marcglasberg authored Mar 27, 2024
2 parents 49c292e + e3a9ed3 commit 35ddd1b
Show file tree
Hide file tree
Showing 6 changed files with 563 additions and 33 deletions.
97 changes: 86 additions & 11 deletions lib/src/ilist/ilist.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,90 @@ import "l_add.dart";
import "l_add_all.dart";
import "l_flat.dart";

/// This is an [IList] which is always empty.
@immutable
class IListEmpty<T> // ignore: must_be_immutable
extends IList<T> {
/// Creates an empty list. In most cases, you should use `const IList.empty()`.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IListEmpty()` which is not constant.
@literal
const IListEmpty._([this.config = const ConfigList()])
: super._gen();

@override
final ConfigList config;

/// An empty list is always flushed, by definition.
@override
bool get isFlushed => true;

/// Nothing happens when you flush a empty list, by definition.
@override
IListEmpty<T> get flush => this;

/// An empty list is always empty, by definition
@override
bool get isEmpty => true;

/// An empty list is always empty, by definition
@override
bool get isNotEmpty => false;

/// An empty list does not contain anything, by definition
@override
bool contains(covariant T? element) => false;

/// An empty list is always of length `0`, by definition
@override
int get length => 0;

/// An empty list has no first element, by definition
@override
Never get first => throw StateError("No element");

/// An empty list has no last element, by definition
@override
Never get last => throw StateError("No element");

/// An empty list has no single element, by definition
@override
Never get single => throw StateError("No element");

/// An empty list is always the reversed version of itself, by definition
@override
IListEmpty<T> get reversed => this;

/// An empty list is always the cleared version of itself, by definition
@override
IListEmpty<T> clear() => this;

@override
int get _counter => 0;

@override
L<T> get _l => LFlat<T>.unsafe([]);

/// Hash codes must be the same for objects that are equal to each other
/// according to operator ==.
@override
int? get _hashCode {
return isDeepEquals
? hash2(const ListEquality<dynamic>().hash([]), config.hashCode)
: hash2(identityHashCode(_l), config.hashCode);
}

@override
set _hashCode(int? value) {}

@override
bool same(IList<T>? other) =>
(other != null) &&
(other is IListEmpty) &&
(config == other.config);
}

/// This is an [IList] which can be made constant.
/// Note: Don't ever use it without the "const" keyword, because it will be unsafe.
///
Expand All @@ -34,15 +118,6 @@ class IListConst<T> // ignore: must_be_immutable
[this.config = const ConfigList()])
: super._gen();

/// Creates a empty constant list.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IListConst.empty()` which is not constant.
@literal
const IListConst.empty([this.config = const ConfigList()])
: _list = const [],
super._gen();

final List<T> _list;

@override
Expand Down Expand Up @@ -200,9 +275,9 @@ abstract class IList<T> // ignore: must_be_immutable
IList.withConfig(iterable ?? const [], defaultConfig);

/// Create an empty [IList].
/// Use it with const: `const IList.empty()` (It's always an [IListConst]).
/// Use it with const: `const IList.empty()` (It's always an [IListEmpty]).
@literal
const factory IList.empty() = IListConst<T>.empty;
const factory IList.empty() = IListEmpty<T>._;

const IList._gen();

Expand Down
93 changes: 82 additions & 11 deletions lib/src/imap/imap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,86 @@ import "m_add_all.dart";
import "m_flat.dart";
import "m_replace.dart";

/// This is an [IMap] which is always empty.
@immutable
class IMapEmpty<K, V> // ignore: must_be_immutable
extends IMap<K, V> {
/// Creates an empty map. In most cases, you should use `const IMap.empty()`.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IMapEmpty()` which is not constant.
@literal
const IMapEmpty._([this.config = const ConfigMap()])
: super._gen();

@override
final ConfigMap config;

/// An empty map is always flushed, by definition.
@override
bool get isFlushed => true;

/// Nothing happens when you flush a empty map, by definition.
@override
IMapEmpty<K, V> get flush => this;

/// An empty map is always empty, by definition
@override
bool get isEmpty => true;

/// An empty map is always empty, by definition
@override
bool get isNotEmpty => false;

/// An empty map does not contain anything, by definition
@override
bool contains(K key, V value) => false;

/// An empty map does not contain anything, by definition
@override
bool containsKey(K? key) => false;

/// An empty map does not contain anything, by definition
@override
bool containsValue(V? value) => false;

/// An empty map does not contain anything, by definition
@override
bool containsEntry(MapEntry<K, V> entry) => false;

/// An empty map is always of length `0`, by definition
@override
int get length => 0;

/// An empty map is always the cleared version of itself, by definition
@override
IMapEmpty<K, V> clear() => this;

@override
int get _counter => 0;

@override
M<K, V> get _m => MFlat<K, V>.unsafe({});

/// Hash codes must be the same for objects that are equal to each other
/// according to operator ==.
@override
int? get _hashCode {
return isDeepEquals
? hash2(const MapEquality<dynamic, dynamic>().hash({}), config.hashCode)
: hash2(identityHashCode(_m), config.hashCode);
}

@override
set _hashCode(int? value) {}

@override
bool same(IMap<K, V>? other) =>
(other != null) &&
(other is IMapEmpty) &&
(config == other.config);
}

/// This is an [IMap] which can be made constant.
/// Note: Don't ever use it without the "const" keyword, because it will be unsafe.
///
Expand All @@ -35,15 +115,6 @@ class IMapConst<K, V> // ignore: must_be_immutable
[this.config = const ConfigMap()])
: super._gen();

/// Creates a empty constant map.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IMapConst.empty()` which is not constant.
@literal
const IMapConst.empty([this.config = const ConfigMap()])
: _map = const {},
super._gen();

final Map<K, V> _map;

@override
Expand Down Expand Up @@ -182,9 +253,9 @@ abstract class IMap<K, V> // ignore: must_be_immutable
IMap.withConfig(map, defaultConfig);

/// Create an empty [IMap].
/// Use it with const: `const IMap.empty()` (It's always an [IMapConst]).
/// Use it with const: `const IMap.empty()` (It's always an [IMapEmpty]).
@literal
const factory IMap.empty() = IMapConst<K, V>.empty;
const factory IMap.empty() = IMapEmpty<K, V>._;

const IMap._gen();

Expand Down
93 changes: 82 additions & 11 deletions lib/src/iset/iset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,86 @@ import "s_add.dart";
import "s_add_all.dart";
import "s_flat.dart";

/// This is an [ISet] which is always empty.
@immutable
class ISetEmpty<T> // ignore: must_be_immutable
extends ISet<T> {
/// Creates an empty set. In most cases, you should use `const ISet.empty()`.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `ISetEmpty()` which is not constant.
@literal
const ISetEmpty._([this.config = const ConfigSet()])
: super._gen();

@override
final ConfigSet config;

/// An empty set is always flushed, by definition.
@override
bool get isFlushed => true;

/// Nothing happens when you flush a empty set, by definition.
@override
ISetEmpty<T> get flush => this;

/// An empty set is always empty, by definition
@override
bool get isEmpty => true;

/// An empty set is always empty, by definition
@override
bool get isNotEmpty => false;

/// An empty set does not contain anything, by definition
@override
bool contains(covariant T? element) => false;

/// An empty set is always of length `0`, by definition
@override
int get length => 0;

/// An empty set has no first element, by definition
@override
Never get first => throw StateError("No element");

/// An empty set has no last element, by definition
@override
Never get last => throw StateError("No element");

/// An empty set has no single element, by definition
@override
Never get single => throw StateError("No element");

/// An empty set is always the cleared version of itself, by definition
@override
ISetEmpty<T> clear() => this;

@override
int get _counter => 0;

@override
S<T> get _s => SFlat<T>.unsafe({});

/// Hash codes must be the same for objects that are equal to each other
/// according to operator ==.
@override
int? get _hashCode {
return isDeepEquals
? hash2(const SetEquality<dynamic>().hash({}), config.hashCode)
: hash2(identityHashCode(_s), config.hashCode);
}

@override
set _hashCode(int? value) {}

@override
bool same(ISet<T>? other) =>
(other != null) &&
(other is ISetEmpty) &&
(config == other.config);
}

/// This is an [ISet] which can be made constant.
/// Note: Don't ever use it without the "const" keyword, because it will be unsafe.
///
Expand All @@ -37,15 +117,6 @@ class ISetConst<T> // ignore: must_be_immutable
[this.config = const ConfigSet()])
: super._gen();

/// Creates a empty constant set.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `ISetConst.empty()` which is not constant.
@literal
const ISetConst.empty([this.config = const ConfigSet()])
: _set = const {},
super._gen();

final Set<T> _set;

@override
Expand Down Expand Up @@ -197,9 +268,9 @@ abstract class ISet<T> // ignore: must_be_immutable
ISet.withConfig(iterable, defaultConfig);

/// Create an empty [ISet].
/// Use it with const: `const ISet.empty()` (It's always an [ISetConst]).
/// Use it with const: `const ISet.empty()` (It's always an [ISetEmpty]).
@literal
const factory ISet.empty() = ISetConst<T>.empty;
const factory ISet.empty() = ISetEmpty<T>._;

const ISet._gen();

Expand Down
Loading

0 comments on commit 35ddd1b

Please sign in to comment.