Skip to content

Commit ea8d35b

Browse files
Roasbeefguggero
authored andcommitted
fn: add new Option[T] type
1 parent c5bd0cc commit ea8d35b

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed

fn/option.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package fn
2+
3+
// Option[A] represents a value which may or may not be there. This is very
4+
// often preferable to nil-able pointers.
5+
type Option[A any] struct {
6+
isSome bool
7+
some A
8+
}
9+
10+
// Some trivially injects a value into an optional context.
11+
//
12+
// Some : A -> Option[A].
13+
func Some[A any](a A) Option[A] {
14+
return Option[A]{
15+
isSome: true,
16+
some: a,
17+
}
18+
}
19+
20+
// None trivially constructs an empty option
21+
//
22+
// None : Option[A].
23+
func None[A any]() Option[A] {
24+
return Option[A]{}
25+
}
26+
27+
// ElimOption is the universal Option eliminator. It can be used to safely
28+
// handle all possible values inside the Option by supplying two continuations.
29+
//
30+
// ElimOption : (Option[A], () -> B, A -> B) -> B.
31+
func ElimOption[A, B any](o Option[A], b func() B, f func(A) B) B {
32+
if o.isSome {
33+
return f(o.some)
34+
}
35+
36+
return b()
37+
}
38+
39+
// UnwrapOr is used to extract a value from an option, and we supply the default
40+
// value in the case when the Option is empty.
41+
//
42+
// UnwrapOr : (Option[A], A) -> A.
43+
func (o Option[A]) UnwrapOr(a A) A {
44+
if o.isSome {
45+
return o.some
46+
}
47+
48+
return a
49+
}
50+
51+
// WhenSome is used to conditionally perform a side-effecting function that
52+
// accepts a value of the type that parameterizes the option. If this function
53+
// performs no side effects, WhenSome is useless.
54+
//
55+
// WhenSome : (Option[A], A -> ()) -> ().
56+
func (o Option[A]) WhenSome(f func(A)) {
57+
if o.isSome {
58+
f(o.some)
59+
}
60+
}
61+
62+
// IsSome returns true if the Option contains a value
63+
//
64+
// IsSome : Option[A] -> bool.
65+
func (o Option[A]) IsSome() bool {
66+
return o.isSome
67+
}
68+
69+
// IsNone returns true if the Option is empty
70+
//
71+
// IsNone : Option[A] -> bool.
72+
func (o Option[A]) IsNone() bool {
73+
return !o.isSome
74+
}
75+
76+
// FlattenOption joins multiple layers of Options together such that if any of
77+
// the layers is None, then the joined value is None. Otherwise the innermost
78+
// Some value is returned.
79+
//
80+
// FlattenOption : Option[Option[A]] -> Option[A].
81+
func FlattenOption[A any](oo Option[Option[A]]) Option[A] {
82+
if oo.IsNone() {
83+
return None[A]()
84+
}
85+
if oo.some.IsNone() {
86+
return None[A]()
87+
}
88+
89+
return oo.some
90+
}
91+
92+
// ChainOption transforms a function A -> Option[B] into one that accepts an
93+
// Option[A] as an argument.
94+
//
95+
// ChainOption : (A -> Option[B]) -> Option[A] -> Option[B].
96+
func ChainOption[A, B any](f func(A) Option[B]) func(Option[A]) Option[B] {
97+
return func(o Option[A]) Option[B] {
98+
if o.isSome {
99+
return f(o.some)
100+
}
101+
102+
return None[B]()
103+
}
104+
}
105+
106+
// MapOption transforms a pure function A -> B into one that will operate
107+
// inside the Option context.
108+
//
109+
// MapOption : (A -> B) -> Option[A] -> Option[B].
110+
func MapOption[A, B any](f func(A) B) func(Option[A]) Option[B] {
111+
return func(o Option[A]) Option[B] {
112+
if o.isSome {
113+
return Some(f(o.some))
114+
}
115+
116+
return None[B]()
117+
}
118+
}
119+
120+
// LiftA2Option transforms a pure function (A, B) -> C into one that will
121+
// operate in an Option context. For the returned function, if either of its
122+
// arguments are None, then the result will be None.
123+
//
124+
// LiftA2Option : ((A, B) -> C) -> (Option[A], Option[B]) -> Option[C].
125+
func LiftA2Option[A, B, C any](
126+
f func(A, B) C) func(Option[A], Option[B]) Option[C] {
127+
128+
return func(o1 Option[A], o2 Option[B]) Option[C] {
129+
if o1.isSome && o2.isSome {
130+
return Some(f(o1.some, o2.some))
131+
}
132+
133+
return None[C]()
134+
}
135+
}
136+
137+
// Alt chooses the left Option if it is full, otherwise it chooses the right
138+
// option. This can be useful in a long chain if you want to choose between
139+
// many different ways of producing the needed value.
140+
//
141+
// Alt : Option[A] -> Option[A] -> Option[A].
142+
func (o Option[A]) Alt(o2 Option[A]) Option[A] {
143+
if o.isSome {
144+
return o
145+
}
146+
147+
return o2
148+
}

0 commit comments

Comments
 (0)