Skip to content

Commit

Permalink
fn: add transpositions for Option and Result
Browse files Browse the repository at this point in the history
  • Loading branch information
ProofOfKeags committed Nov 6, 2024
1 parent 8971c4c commit 5dec354
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
12 changes: 12 additions & 0 deletions fn/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,15 @@ func (o Option[A]) SomeToOkf(errString string, args ...interface{}) Result[A] {
OptionToLeft[A, A, error](o, fmt.Errorf(errString, args...)),
}
}

// TransposeOptRes transposes the Option[Result[A]] into a Result[Option[A]].
// This has the effect of leaving an A value alone while inverting the Option
// and Result layers. If there is no internal A value, it will convert the
// non-success value to the proper one in the transposition.
func TransposeOptRes[A any](o Option[Result[A]]) Result[Option[A]] {
if o.IsNone() {
return Ok(None[A]())
}

return Result[Option[A]]{MapLeft[A, error](Some[A])(o.some.Either)}
}
25 changes: 25 additions & 0 deletions fn/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"testing"
"testing/quick"

"github.com/stretchr/testify/require"
)
Expand All @@ -26,3 +27,27 @@ func TestSomeToOkf(t *testing.T) {
Err[uint8](fmt.Errorf(errStr)),
)
}

func TestPropTransposeOptResInverts(t *testing.T) {
f := func(i uint) bool {
var o Option[Result[uint]]
switch i % 3 {
case 0:
o = Some(Ok(i))
case 1:
o = Some(Errf[uint]("error"))
case 2:
o = None[Result[uint]]()
default:
return false
}

odd := TransposeOptRes(o) ==
TransposeOptRes(TransposeResOpt(TransposeOptRes(o)))
even := TransposeResOpt(TransposeOptRes(o)) == o

return odd && even
}

require.NoError(t, quick.Check(f, nil))
}
12 changes: 12 additions & 0 deletions fn/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,15 @@ func AndThen2[A, B, C any](ra Result[A], rb Result[B],
})
})
}

// TransposeResOpt transposes the Result[Option[A]] into a Option[Result[A]].
// This has the effect of leaving an A value alone while inverting the Result
// and Option layers. If there is no internal A value, it will convert the
// non-success value to the proper one in the transposition.
func TransposeResOpt[A any](r Result[Option[A]]) Option[Result[A]] {
if r.IsErr() {
return Some(Err[A](r.right))
}

return MapOption(Ok[A])(r.left)
}
25 changes: 25 additions & 0 deletions fn/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,28 @@ func TestFlattenResult(t *testing.T) {

require.NoError(t, quick.Check(f, nil))
}

func TestPropTransposeResOptInverts(t *testing.T) {
f := func(i uint) bool {
var r Result[Option[uint]]
switch i % 3 {
case 0:
r = Ok(Some(i))
case 1:
r = Ok(None[uint]())
case 2:
r = Errf[Option[uint]]("error")
default:
return false
}

odd := TransposeResOpt(TransposeOptRes(TransposeResOpt(r))) ==
TransposeResOpt(r)

even := TransposeOptRes(TransposeResOpt(r)) == r

return odd && even
}

require.NoError(t, quick.Check(f, nil))
}

0 comments on commit 5dec354

Please sign in to comment.