Skip to content

Commit b221c67

Browse files
authored
Merge pull request #1 from repeale/feat/option-implementation
feat: Option implementation
2 parents 173a9f6 + c0f8d57 commit b221c67

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Fp-go is a collection of Functional Programming helpers powered by Golang [1.18]
3131
- [Compose](#compose)
3232
- [Pipe](#pipe)
3333
- [Curry](#curry)
34+
- [Structs](#structs)
35+
- [Option](#option)
3436

3537
## Install
3638

@@ -232,3 +234,12 @@ Variations `Curry2`, `Curry3` and `Curry4` stating the number of params will be
232234
curryedSum := Curry2(func(a int, b int) int { return a + b })
233235
curryedSum(1)(2)
234236
```
237+
238+
## Structs
239+
240+
#### Option
241+
242+
Option represents encapsulation of an optional value, it might be used as the return type of functions which may or may not return a meaningful value when they are applied.
243+
You could instanciate an `opt.Option[T]` with a value with `opt.Some(val)`. If the value is missing you can use `opt.None[T]()`.
244+
245+
Option exports `Some`, `None`, `IsSome`, `IsNone`, `GetOrElse`, `Match`, `Map`, `Chain`.

function.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package fp
2+
3+
// Callback function that returns a specific value type
4+
type Lazy[T any] func() T
5+
6+
// Callback function that takes an argument and return a value of the same type
7+
type LazyVal[T any] func(x T) T

option/option.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package opt
2+
3+
import (
4+
"github.com/repeale/fp-go"
5+
)
6+
7+
// Base option struct
8+
type Option[T any] struct {
9+
value T
10+
hasValue bool
11+
}
12+
13+
// Constructor for Option with a value
14+
func Some[T any](value T) Option[T] {
15+
return Option[T]{value, true}
16+
}
17+
18+
// Constructor for Option without a value
19+
func None[T any]() Option[T] {
20+
return Option[T]{}
21+
}
22+
23+
// Helper to check if the Option has a value
24+
func IsSome[T any](option Option[T]) bool {
25+
return option.hasValue
26+
}
27+
28+
// Helper to check if the Option is missing the value
29+
func IsNone[T any](option Option[T]) bool {
30+
return !option.hasValue
31+
}
32+
33+
// Extracts the value out of the Option, if it exists. Otherwise returns the function with a default value
34+
func GetOrElse[T any](onNone fp.Lazy[T]) func(Option[T]) T {
35+
return func(option Option[T]) T {
36+
37+
if IsNone(option) {
38+
return onNone()
39+
}
40+
41+
return option.value
42+
}
43+
}
44+
45+
// Extracts the value out of the Option, if it exists, with a function. Otherwise returns the function with a default value
46+
func Match[T any](onNone fp.Lazy[T], onSome fp.LazyVal[T]) func(Option[T]) T {
47+
return func(option Option[T]) T {
48+
49+
if IsNone(option) {
50+
return onNone()
51+
}
52+
53+
return onSome(option.value)
54+
}
55+
}
56+
57+
// Execute the function on the Option value if it exists. Otherwise return the empty Option itself
58+
func Map[T any](fn fp.LazyVal[T]) func(o Option[T]) Option[T] {
59+
return func(option Option[T]) Option[T] {
60+
61+
if IsNone(option) {
62+
return None[T]()
63+
}
64+
65+
return Some(fn(option.value))
66+
}
67+
}
68+
69+
// Execute a function that returns an Option on the Option value if it exists. Otherwise return the empty Option itself
70+
func Chain[A any, B any](fn func(a A) Option[B]) func(Option[A]) Option[B] {
71+
return func(a Option[A]) Option[B] {
72+
73+
if IsNone(a) {
74+
return None[B]()
75+
}
76+
77+
return fn(a.value)
78+
}
79+
}

option/option_test.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package opt
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestSome(t *testing.T) {
8+
res := Some("val")
9+
if res.hasValue != true {
10+
t.Error("Some should return a struct with hasValue set to true. Received:", res.hasValue)
11+
}
12+
}
13+
14+
func TestNone(t *testing.T) {
15+
res := None[string]()
16+
if res.hasValue != false {
17+
t.Error("None should return a struct with hasValue set to false. Received:", res.hasValue)
18+
}
19+
}
20+
21+
func TestIsSome_Some(t *testing.T) {
22+
res := IsSome(Some("value"))
23+
if res != true {
24+
t.Error("IsSome should return true. Received:", res)
25+
}
26+
}
27+
func TestIsSome_None(t *testing.T) {
28+
res := IsSome(None[string]())
29+
if res != false {
30+
t.Error("IsSome should return false. Received:", res)
31+
}
32+
}
33+
34+
func TestIsNone_Some(t *testing.T) {
35+
res := IsNone(None[string]())
36+
if res != true {
37+
t.Error("IsNone should return true. Received:", res)
38+
}
39+
}
40+
func TestIsNone_None(t *testing.T) {
41+
res := IsNone(Some("value"))
42+
if res != false {
43+
t.Error("IsNone should return false. Received:", res)
44+
}
45+
}
46+
47+
func TestGetOrElse_Some(t *testing.T) {
48+
res := GetOrElse(func() string { return "fail" })(Some("val"))
49+
if res != "val" {
50+
t.Error("GetOrElse should return the Some value. Received:", res)
51+
}
52+
}
53+
54+
func TestGetOrElse_None(t *testing.T) {
55+
res := GetOrElse(func() string { return "elseValue" })(None[string]())
56+
if res != "elseValue" {
57+
t.Error("GetOrElse should return the onNone() value. Received:", res)
58+
}
59+
}
60+
61+
func TestMatch_onSome(t *testing.T) {
62+
res := Match(func() string { return "onNone" }, func(x string) string { return x + x })(Some("val"))
63+
if res != "valval" {
64+
t.Error("Match should return the onSome() value. Received:", res)
65+
}
66+
}
67+
68+
func TestMatch_onNone(t *testing.T) {
69+
res := Match(func() string { return "onNone" }, func(x string) string { return x + x })(None[string]())
70+
if res != "onNone" {
71+
t.Error("Match should return the onNone() return value. Received:", res)
72+
}
73+
}
74+
75+
func TestMap_Some(t *testing.T) {
76+
res := Map(func(x string) string { return x + x })(Some("val"))
77+
if res.value != "valval" {
78+
t.Error("Map should return the result of the callback function. Received:", res.value)
79+
}
80+
}
81+
82+
func TestMap_None(t *testing.T) {
83+
res := Map(func(x string) string { return x + x })(None[string]())
84+
if res.hasValue != false {
85+
t.Error("Map should return a None value. Received:", res.value)
86+
}
87+
}
88+
89+
func TestChain_Some(t *testing.T) {
90+
res := Chain(func(x string) Option[string] { return Some(x + x) })(Some("val"))
91+
if res.hasValue != true {
92+
t.Error("Chain should return a Some of string. Received:", res.value)
93+
}
94+
}
95+
func TestChain_None(t *testing.T) {
96+
res := Chain(func(x string) Option[string] { return Some(x + x) })(None[string]())
97+
if res.hasValue != false {
98+
t.Error("Chain should return a None value. Received:", res.value)
99+
}
100+
}

0 commit comments

Comments
 (0)