-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOption.cs
More file actions
198 lines (159 loc) · 7.21 KB
/
Option.cs
File metadata and controls
198 lines (159 loc) · 7.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Text;
namespace MicroUtils
{
public readonly struct Option<T> : IEquatable<Option<T>>, IEquatable<T?>, IEnumerable<T> where T : notnull
{
public readonly T MaybeValue;
public T Value => MaybeValue ?? throw new NullReferenceException();
public readonly bool IsSome;
public bool IsNone => !IsSome;
Option(T value)
{
MaybeValue = value;
IsSome = true;
}
public Option()
{
MaybeValue = default!;
}
public static Option<T> Some(T value) => new(value);
public static readonly Option<T> None = new();
public override string ToString() => this.IsSome ? $"Some {this.Value}" : "None";
public bool Equals(Option<T> other)
{
if (this.IsNone)
return other.IsNone;
return this.MaybeValue?.Equals(other.MaybeValue) ??
throw new InvalidOperationException(
$"null is an invalid value for {typeof(Option<T>)}.{nameof(Some)}");
}
public static bool operator ==(Option<T> a, Option<T> b) => a.Equals(b);
public static bool operator !=(Option<T> a, Option<T> b) => !a.Equals(b);
public bool Equals(T? other) => this switch
{
var some when some.IsSome => some.Value.Equals(other),
_ => other is null,
};
public static bool operator ==(Option<T> a, T? b) => a.Equals(b);
public static bool operator !=(Option<T> a, T? b) => !a.Equals(b);
public static bool operator ==(T? a, Option<T> b) => b.Equals(a);
public static bool operator !=(T? a, Option<T> b) => !b.Equals(a);
public override bool Equals(object? obj) =>
base.Equals(obj) ||
(obj is Option<T> option && this.Equals(option)) ||
(this.MaybeValue is null && obj is null) ||
(this.MaybeValue?.Equals(obj) ?? false);
public override int GetHashCode() => HashCode.Combine(MaybeValue);
public IEnumerator<T> GetEnumerator()
{
if (IsSome)
yield return MaybeValue!;
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
public static class Option
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<T> Some<T>(T value) where T : notnull => Option<T>.Some(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<T> None<T>() where T : notnull => Option<T>.None;
public static Option<T> OfObj<T>(T? obj) where T : notnull =>
obj switch
{
not null => Some(obj),
_ => None<T>()
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<T> ToOption<T>(this T? obj) where T : notnull => OfObj(obj);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? ToObj<T>(Option<T> option) where T : notnull => option.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsSome<T>(Option<T> option) where T : notnull => option.IsSome;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNone<T>(Option<T> option) where T : notnull => option.IsNone;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<A> Return<A>(A value) where A : notnull => Some(value);
public static Func<Option<A>, Option<B>> Bind<A, B>(Func<A, Option<B>> binder)
where A : notnull
where B : notnull =>
option => option switch
{
var some when some.IsSome => binder(some.Value),
_ => Option<B>.None
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<B> Bind<A, B>(this Option<A> option, Func<A, Option<B>> binder)
where A : notnull
where B : notnull =>
Bind(binder)(option);
public static Func<Option<A>, Option<B>> Lift<A, B>(Func<A, B> f)
where A : notnull
where B : notnull =>
option => option switch
{
var some when some.IsSome => ToOption(f(some.Value)),
_ => Option<B>.None
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<B> Map<A, B>(this Option<A> option, Func<A, B> f)
where A : notnull
where B : notnull =>
Lift(f)(option);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<A> OrElseWith<A>(this Option<A> option, Func<Option<A>> orElseThunk)
where A : notnull =>
option.IsSome ? option : orElseThunk();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<A> OrElse<A>(this Option<A> option, Option<A> orElse)
where A : notnull =>
option.IsSome ? option : orElse;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Func<Option<A>, Option<B>> Apply<A, B>(Option<Func<A, B>> lifted)
where A : notnull
where B : notnull =>
option => lifted.Bind(f => option.Map(f));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<B> Apply<A, B>(this Option<Func<A, B>> lifted, Option<A> option)
where A : notnull
where B : notnull =>
Apply(lifted)(option);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<C> Apply2<A, B, C>(this Option<Func<A, B, C>> lifted, Option<A> optionA, Option<B> optionB)
where A : notnull
where B : notnull
where C : notnull =>
lifted.Map(Functional.Curry).Apply(optionA).Apply(optionB);
public static IEnumerable<U> Choose<T, U>(this IEnumerable<T> source, Func<T, Option<U>> chooser) where U : notnull =>
source.SelectMany(x => chooser(x));
public static Option<T> TryHead<T>(this IEnumerable<T> source) where T : notnull
{
foreach (var x in source)
return Some(x);
return None<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Option<T> TryFind<T>(this IEnumerable<T> source, Func<T, bool> predicate) where T : notnull =>
source.Where(predicate).TryHead();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DefaultWith<T>(this Option<T> option, Func<T> defaultThunk)
where T : notnull =>
option switch
{
var some when some.IsSome => some.Value,
_ => defaultThunk()
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DefaultValue<T>(this Option<T> option, T defaultValue) where T : notnull =>
option switch
{
var some when some.IsSome => some.Value,
_ => defaultValue
};
}
}