Skip to content

Commit

Permalink
automatic BiMap for superset category (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
fogfish authored Oct 31, 2024
1 parent b320083 commit 091a28c
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
2 changes: 2 additions & 0 deletions optics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ See runnable examples to play with the library

* optics/v0.10 and earlier uses `reflect`
* optics/v0.11 and later uses `unsafe` pointers
* optics/v0.12 `isomorphism`
* optics/v0.13 auto `bimap` for superset category

## References

Expand Down
56 changes: 56 additions & 0 deletions optics/iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,62 @@ type codec[S, A, B any] struct {
func (c codec[S, A, B]) Put(s *S, b B) *S { return c.lens.Put(s, c.cmap(b)) }
func (c codec[S, A, B]) Get(s *S) B { return c.fmap(c.lens.Get(s)) }

// Automatic transformer for string superset, preserve mapping between
// two categories A, B, where both rooted to strings
func BiMapS[S any, A, B String](attr ...string) Lens[S, B] {
return BiMap(
ForProduct1[S, A](attr...),
func(a A) B { return B(a) },
func(b B) A { return A(b) },
)
}

type String interface {
~string
}

// Automatic transformer for []byte superset, preserve mapping between
// two categories A, B, where both rooted to []byte
func BiMapB[S any, A, B Byte](attr ...string) Lens[S, B] {
return BiMap(
ForProduct1[S, A](attr...),
func(a A) B { return B(a) },
func(b B) A { return A(b) },
)
}

type Byte interface {
~[]byte
}

// Automatic transformer for int superset, preserve mapping between
// two categories A, B, where both rooted to int
func BiMapI[S any, A, B Int](attr ...string) Lens[S, B] {
return BiMap(
ForProduct1[S, A](attr...),
func(a A) B { return B(a) },
func(b B) A { return A(b) },
)
}

type Int interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}

// Automatic transformer for float32 superset, preserve mapping between
// two categories A, B, where both rooted to float
func BiMapF[S any, A, B Float](attr ...string) Lens[S, B] {
return BiMap(
ForProduct1[S, A](attr...),
func(a A) B { return B(a) },
func(b B) A { return A(b) },
)
}

type Float interface {
~float32 | ~float64
}

// An isomorphism is a structure-preserving mapping between two structures
// of the same shape that can be reversed by an inverse mapping.
type Isomorphism[S, T any] interface {
Expand Down
71 changes: 71 additions & 0 deletions optics/iso_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,74 @@ func TestIsomorphism(t *testing.T) {

t.Run("Interface", testIsomorphism[io.Reader](&bytes.Buffer{}))
}

func TestBiMapX(t *testing.T) {
t.Run("String", func(t *testing.T) {
type A string
type B string
type S struct{ A }

bimap := optics.BiMapS[S, A, B]()

s := S{}
bimap.Put(&s, "some")
b := bimap.Get(&s)

it.Then(t).Should(
it.Equal(b, "some"),
it.Equal(s.A, "some"),
)
})

t.Run("Byte", func(t *testing.T) {
type A []byte
type B []byte
type S struct{ A }

bimap := optics.BiMapB[S, A, B]()

s := S{}
bimap.Put(&s, []byte("some"))
b := bimap.Get(&s)

it.Then(t).Should(
it.Equiv(b, []byte("some")),
it.Equiv(s.A, []byte("some")),
)
})

t.Run("Int", func(t *testing.T) {
type A int
type B int
type S struct{ A }

bimap := optics.BiMapI[S, A, B]()

s := S{}
bimap.Put(&s, 100)
b := bimap.Get(&s)

it.Then(t).Should(
it.Equal(b, 100),
it.Equal(s.A, 100),
)
})

t.Run("Float", func(t *testing.T) {
type A float64
type B float64
type S struct{ A }

bimap := optics.BiMapF[S, A, B]()

s := S{}
bimap.Put(&s, 100.0)
b := bimap.Get(&s)

it.Then(t).Should(
it.Equal(b, 100.0),
it.Equal(s.A, 100.0),
)
})

}
2 changes: 1 addition & 1 deletion optics/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

package optics

const Version = "optics/v0.12.1"
const Version = "optics/v0.13.0"

0 comments on commit 091a28c

Please sign in to comment.