Skip to content

Commit 1b30e02

Browse files
author
Dean Karn
authored
And then (#41)
Add `And` + `AndThen` functions to `Option` and `Result` for in place manipulation.
1 parent c86469e commit 1b30e02

File tree

6 files changed

+77
-3
lines changed

6 files changed

+77
-3
lines changed

.github/workflows/go.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ on:
55
- master
66
pull_request:
77
types: [opened, edited, reopened, synchronize]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
11+
cancel-in-progress: true
12+
813
jobs:
914
test:
1015
strategy:
1116
matrix:
12-
go-version: [1.20.x,1.19.x,1.18.x,1.17.x]
17+
go-version: [1.21.x,1.20.x,1.19.x,1.18.x,1.17.x]
1318
os: [ubuntu-latest, macos-latest, windows-latest]
1419
runs-on: ${{ matrix.os }}
1520
steps:

CHANGELOG.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9-
## [5.21.3] - 2023-10-18
9+
## [5.23.0] - 2024-01-14
10+
### Added
11+
- `And` and `AndThen` functions to `Option` & `Result` types.
12+
13+
## [5.22.0] - 2023-10-18
1014
### Added
1115
- `UnwrapOr`, `UnwrapOrElse` and `UnwrapOrDefault` functions to `Option` & `Result` types.
1216

@@ -82,7 +86,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8286
### Added
8387
- Added `timext.NanoTime` for fast low level monotonic time with nanosecond precision.
8488

85-
[Unreleased]: https://github.com/go-playground/pkg/compare/v5.22.0...HEAD
89+
[Unreleased]: https://github.com/go-playground/pkg/compare/v5.23.0...HEAD
90+
[5.23.0]: https://github.com/go-playground/pkg/compare/v5.22.0..v5.23.0
8691
[5.22.0]: https://github.com/go-playground/pkg/compare/v5.21.3..v5.22.0
8792
[5.21.3]: https://github.com/go-playground/pkg/compare/v5.21.2..v5.21.3
8893
[5.21.2]: https://github.com/go-playground/pkg/compare/v5.21.1..v5.21.2

values/option/option.go

+19
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,25 @@ func (o Option[T]) UnwrapOrDefault() T {
9393
return o.value
9494
}
9595

96+
// And calls the provided function with the contained value if the option is Some, returns the None value otherwise.
97+
func (o Option[T]) And(fn func(T) T) Option[T] {
98+
if o.isSome {
99+
o.value = fn(o.value)
100+
}
101+
return o
102+
}
103+
104+
// AndThen calls the provided function with the contained value if the option is Some, returns the None value otherwise.
105+
//
106+
// This differs from `And` in that the provided function returns an Option[T] allowing changing of the Option value
107+
// itself.
108+
func (o Option[T]) AndThen(fn func(T) Option[T]) Option[T] {
109+
if o.isSome {
110+
return fn(o.value)
111+
}
112+
return o
113+
}
114+
96115
// Some creates a new Option with the given values.
97116
func Some[T any](value T) Option[T] {
98117
return Option[T]{value, true}

values/option/option_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ type testStructType struct {
2626
Name string
2727
}
2828

29+
func TestAndXXX(t *testing.T) {
30+
s := Some(1)
31+
Equal(t, Some(3), s.And(func(i int) int { return 3 }))
32+
Equal(t, Some(3), s.AndThen(func(i int) Option[int] { return Some(3) }))
33+
Equal(t, None[int](), s.AndThen(func(i int) Option[int] { return None[int]() }))
34+
35+
n := None[int]()
36+
Equal(t, None[int](), n.And(func(i int) int { return 3 }))
37+
Equal(t, None[int](), n.AndThen(func(i int) Option[int] { return Some(3) }))
38+
Equal(t, None[int](), n.AndThen(func(i int) Option[int] { return None[int]() }))
39+
Equal(t, None[int](), s.AndThen(func(i int) Option[int] { return None[int]() }))
40+
}
41+
2942
func TestUnwraps(t *testing.T) {
3043
none := None[int]()
3144
PanicMatches(t, func() { none.Unwrap() }, "Option.Unwrap: option is None")

values/result/result.go

+19
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,25 @@ func (r Result[T, E]) UnwrapOrDefault() T {
6262
return r.ok
6363
}
6464

65+
// And calls the provided function with the contained value if the result is Ok, returns the Result value otherwise.
66+
func (r Result[T, E]) And(fn func(T) T) Result[T, E] {
67+
if r.isOk {
68+
r.ok = fn(r.ok)
69+
}
70+
return r
71+
}
72+
73+
// AndThen calls the provided function with the contained value if the result is Ok, returns the Result value otherwise.
74+
//
75+
// This differs from `And` in that the provided function returns a Result[T, E] allowing changing of the Option value
76+
// itself.
77+
func (r Result[T, E]) AndThen(fn func(T) Result[T, E]) Result[T, E] {
78+
if r.isOk {
79+
return fn(r.ok)
80+
}
81+
return r
82+
}
83+
6584
// Err returns the error of the result. To be used after calling IsOK()
6685
func (r Result[T, E]) Err() E {
6786
return r.err

values/result/result_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ import (
1313

1414
type myStruct struct{}
1515

16+
func TestAndXXX(t *testing.T) {
17+
ok := Ok[int, error](1)
18+
Equal(t, Ok[int, error](3), ok.And(func(int) int { return 3 }))
19+
Equal(t, Ok[int, error](3), ok.AndThen(func(int) Result[int, error] { return Ok[int, error](3) }))
20+
Equal(t, Err[int, error](io.EOF), ok.AndThen(func(int) Result[int, error] { return Err[int, error](io.EOF) }))
21+
22+
err := Err[int, error](io.EOF)
23+
Equal(t, Err[int, error](io.EOF), err.And(func(int) int { return 3 }))
24+
Equal(t, Err[int, error](io.EOF), err.AndThen(func(int) Result[int, error] { return Ok[int, error](3) }))
25+
Equal(t, Err[int, error](io.EOF), err.AndThen(func(int) Result[int, error] { return Err[int, error](io.ErrUnexpectedEOF) }))
26+
Equal(t, Err[int, error](io.ErrUnexpectedEOF), ok.AndThen(func(int) Result[int, error] { return Err[int, error](io.ErrUnexpectedEOF) }))
27+
}
28+
1629
func TestUnwrap(t *testing.T) {
1730
er := Err[int, error](io.EOF)
1831
PanicMatches(t, func() { er.Unwrap() }, "Result.Unwrap(): result is Err")

0 commit comments

Comments
 (0)