Skip to content

Commit b491bd7

Browse files
gnpricechrisbobbe
authored andcommitted
basic: Add a generic Option class
1 parent f81a698 commit b491bd7

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

lib/basic.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
/// Either a value, or the absence of a value.
3+
///
4+
/// An `Option<T>` is either an `OptionSome` representing a `T` value,
5+
/// or an `OptionNone` representing the absence of a value.
6+
///
7+
/// When `T` is non-nullable, this is the same information that is
8+
/// normally represented as a `T?`.
9+
/// This class is useful when T is nullable (or might be nullable).
10+
/// In that case `null` is already a T value,
11+
/// and so can't also be used to represent the absence of a T value,
12+
/// but `OptionNone()` is a different value from `OptionSome(null)`.
13+
///
14+
/// This interface is small because members are added lazily when needed.
15+
/// If adding another member, consider borrowing the naming from Rust:
16+
/// https://doc.rust-lang.org/std/option/enum.Option.html
17+
sealed class Option<T> {
18+
const Option();
19+
20+
/// The value contained in this option, if any; else the given value.
21+
T or(T optb);
22+
}
23+
24+
class OptionNone<T> extends Option<T> {
25+
const OptionNone();
26+
27+
@override
28+
T or(T optb) => optb;
29+
30+
@override
31+
bool operator ==(Object other) => other is OptionNone;
32+
33+
@override
34+
int get hashCode => 'OptionNone'.hashCode;
35+
36+
@override
37+
String toString() => 'OptionNone';
38+
}
39+
40+
class OptionSome<T> extends Option<T> {
41+
const OptionSome(this.value);
42+
43+
final T value;
44+
45+
@override
46+
T or(T optb) => value;
47+
48+
@override
49+
bool operator ==(Object other) => other is OptionSome && value == other.value;
50+
51+
@override
52+
int get hashCode => Object.hash('OptionSome', value);
53+
54+
@override
55+
String toString() => 'OptionSome($value)';
56+
}

test/basic_test.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:checks/checks.dart';
2+
import 'package:test/scaffolding.dart';
3+
import 'package:zulip/basic.dart';
4+
5+
void main() {
6+
group('Option', () {
7+
test('==/hashCode', () {
8+
void checkEqual(Object a, Object b) {
9+
check(a).equals(b);
10+
check(a.hashCode).equals(b.hashCode);
11+
}
12+
13+
void checkUnequal(Object a, Object b) {
14+
check(a).not((it) => it.equals(b));
15+
}
16+
17+
checkEqual(OptionNone<int>(), OptionNone<int?>());
18+
checkEqual(OptionNone<int>(), OptionNone<String>());
19+
20+
checkEqual(OptionSome<int>(3), OptionSome<int?>(3));
21+
checkEqual(OptionSome<int?>(null), OptionSome<String?>(null));
22+
checkEqual(OptionSome(OptionSome(3)), OptionSome(OptionSome(3)));
23+
24+
checkUnequal(OptionNone<void>(), OptionSome<void>(null));
25+
checkUnequal(OptionSome(3), OptionSome(OptionSome(3)));
26+
checkUnequal(3, OptionSome(3));
27+
});
28+
29+
test('or', () {
30+
check(OptionSome<int>(3).or(4)).equals(3);
31+
check(OptionSome<int?>(3).or(4)).equals(3);
32+
check(OptionSome<int?>(null).or(4)).equals(null);
33+
check(OptionNone<int?>().or(4)).equals(4);
34+
});
35+
});
36+
}

0 commit comments

Comments
 (0)