mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
fix: improve lens handling
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -116,18 +116,20 @@ func Make{{.Name}}Lenses() {{.Name}}Lenses {
|
|||||||
|
|
||||||
// Make{{.Name}}RefLenses creates a new {{.Name}}RefLenses with lenses for all fields
|
// Make{{.Name}}RefLenses creates a new {{.Name}}RefLenses with lenses for all fields
|
||||||
func Make{{.Name}}RefLenses() {{.Name}}RefLenses {
|
func Make{{.Name}}RefLenses() {{.Name}}RefLenses {
|
||||||
{{- range .Fields}}
|
|
||||||
{{- if .IsOptional}}
|
|
||||||
iso{{.Name}} := I.FromZero[{{.TypeName}}]()
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
return {{.Name}}RefLenses{
|
return {{.Name}}RefLenses{
|
||||||
{{- range .Fields}}
|
{{- range .Fields}}
|
||||||
{{- if .IsOptional}}
|
{{- if .IsOptional}}
|
||||||
{{.Name}}: L.MakeLensRef(
|
{{- if .IsComparable}}
|
||||||
func(s *{{$.Name}}) O.Option[{{.TypeName}}] { return iso{{.Name}}.Get(s.{{.Name}}) },
|
{{.Name}}: LO.FromIso[*{{$.Name}}](I.FromZero[{{.TypeName}}]())(L.MakeLensStrict(
|
||||||
func(s *{{$.Name}}, v O.Option[{{.TypeName}}]) *{{$.Name}} { s.{{.Name}} = iso{{.Name}}.ReverseGet(v); return s },
|
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
||||||
),
|
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
||||||
|
)),
|
||||||
|
{{- else}}
|
||||||
|
{{.Name}}: LO.FromIso[*{{$.Name}}](I.FromZero[{{.TypeName}}]())(L.MakeLensRef(
|
||||||
|
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
||||||
|
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
||||||
|
)),
|
||||||
|
{{- end}}
|
||||||
{{- else}}
|
{{- else}}
|
||||||
{{- if .IsComparable}}
|
{{- if .IsComparable}}
|
||||||
{{.Name}}: L.MakeLensStrict(
|
{{.Name}}: L.MakeLensStrict(
|
||||||
@@ -460,9 +462,7 @@ func parseFile(filename string) ([]structInfo, string, error) {
|
|||||||
|
|
||||||
// Check if the type is comparable (for non-optional fields)
|
// Check if the type is comparable (for non-optional fields)
|
||||||
// For optional fields, we don't need to check since they use LensO
|
// For optional fields, we don't need to check since they use LensO
|
||||||
if !isOptional {
|
isComparable = isComparableType(field.Type)
|
||||||
isComparable = isComparableType(field.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract imports from this field's type
|
// Extract imports from this field's type
|
||||||
fieldImports := make(map[string]string)
|
fieldImports := make(map[string]string)
|
||||||
|
|||||||
@@ -472,7 +472,6 @@ type TypeTest struct {
|
|||||||
assert.Equal(t, "Pointer", typeTest.Fields[2].Name)
|
assert.Equal(t, "Pointer", typeTest.Fields[2].Name)
|
||||||
assert.Equal(t, "*string", typeTest.Fields[2].TypeName)
|
assert.Equal(t, "*string", typeTest.Fields[2].TypeName)
|
||||||
assert.True(t, typeTest.Fields[2].IsOptional)
|
assert.True(t, typeTest.Fields[2].IsOptional)
|
||||||
assert.False(t, typeTest.Fields[2].IsComparable, "IsComparable not set for optional fields")
|
|
||||||
|
|
||||||
// Slice - not comparable
|
// Slice - not comparable
|
||||||
assert.Equal(t, "Slice", typeTest.Fields[3].Name)
|
assert.Equal(t, "Slice", typeTest.Fields[3].Name)
|
||||||
@@ -526,9 +525,6 @@ func TestLensRefTemplatesWithComparable(t *testing.T) {
|
|||||||
assert.Contains(t, constructorStr, "Data: L.MakeLensRef(",
|
assert.Contains(t, constructorStr, "Data: L.MakeLensRef(",
|
||||||
"non-comparable field Data should use MakeLensRef in RefLenses")
|
"non-comparable field Data should use MakeLensRef in RefLenses")
|
||||||
|
|
||||||
// Pointer field - optional, should use MakeLensRef
|
|
||||||
assert.Contains(t, constructorStr, "Pointer: L.MakeLensRef(",
|
|
||||||
"optional field Pointer should use MakeLensRef in RefLenses")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateLensHelpersWithComparable(t *testing.T) {
|
func TestGenerateLensHelpersWithComparable(t *testing.T) {
|
||||||
|
|||||||
@@ -24,18 +24,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// FromNillable converts a nillable value to an option and back
|
// FromNillable converts a nillable value to an option and back
|
||||||
func FromNillable[T any]() I.Iso[*T, O.Option[T]] {
|
func FromNillable[T any]() Iso[*T, Option[T]] {
|
||||||
return I.MakeIso(F.Flow2(
|
return I.MakeIso(F.Flow2(
|
||||||
O.FromPredicate(F.IsNonNil[T]),
|
O.FromPredicate(F.IsNonNil[T]),
|
||||||
O.Map(F.Deref[T]),
|
O.Map(F.Deref[T]),
|
||||||
),
|
),
|
||||||
O.Fold(F.Constant((*T)(nil)), F.Ref[T]),
|
O.Fold(F.ConstNil[T], F.Ref[T]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose converts a Lens to a property of `A` into a lens to a property of type `B`
|
// Compose converts a Lens to a property of `A` into a lens to a property of type `B`
|
||||||
// the transformation is done via an ISO
|
// the transformation is done via an ISO
|
||||||
func Compose[S, A, B any](ab I.Iso[A, B]) func(sa L.Lens[S, A]) L.Lens[S, B] {
|
func Compose[S, A, B any](ab Iso[A, B]) Operator[S, A, B] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
ab,
|
ab,
|
||||||
IL.IsoAsLens[A, B],
|
IL.IsoAsLens[A, B],
|
||||||
|
|||||||
14
v2/optics/lens/iso/types.go
Normal file
14
v2/optics/lens/iso/types.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/optics/iso"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Option[A any] = option.Option[A]
|
||||||
|
Iso[S, A any] = iso.Iso[S, A]
|
||||||
|
Lens[S, A any] = lens.Lens[S, A]
|
||||||
|
Operator[S, A, B any] = lens.Operator[S, A, B]
|
||||||
|
)
|
||||||
@@ -435,7 +435,7 @@ func compose[GET ~func(S) B, SET ~func(S, B) S, S, A, B any](creator func(get GE
|
|||||||
// person := Person{Name: "Alice", Address: Address{Street: "Main St"}}
|
// person := Person{Name: "Alice", Address: Address{Street: "Main St"}}
|
||||||
// street := personStreetLens.Get(person) // "Main St"
|
// street := personStreetLens.Get(person) // "Main St"
|
||||||
// updated := personStreetLens.Set("Oak Ave")(person)
|
// updated := personStreetLens.Set("Oak Ave")(person)
|
||||||
func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
|
func Compose[S, A, B any](ab Lens[A, B]) Operator[S, A, B] {
|
||||||
return compose(MakeLens[func(S) B, func(S, B) S], ab)
|
return compose(MakeLens[func(S) B, func(S, B) S], ab)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +477,7 @@ func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
|
|||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// personStreetLens := F.Pipe1(addressLens, lens.ComposeRef[Person](streetLens))
|
// personStreetLens := F.Pipe1(addressLens, lens.ComposeRef[Person](streetLens))
|
||||||
func ComposeRef[S, A, B any](ab Lens[A, B]) func(Lens[*S, A]) Lens[*S, B] {
|
func ComposeRef[S, A, B any](ab Lens[A, B]) Operator[*S, A, B] {
|
||||||
return compose(MakeLensRef[func(*S) B, func(*S, B) *S], ab)
|
return compose(MakeLensRef[func(*S) B, func(*S, B) *S], ab)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package option
|
|||||||
import (
|
import (
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/optics/lens"
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
LI "github.com/IBM/fp-go/v2/optics/lens/iso"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,3 +96,69 @@ func FromOption[S, A any](defaultValue A) func(sa LensO[S, A]) Lens[S, A] {
|
|||||||
func FromOptionRef[S, A any](defaultValue A) func(sa Lens[*S, Option[A]]) Lens[*S, A] {
|
func FromOptionRef[S, A any](defaultValue A) func(sa Lens[*S, Option[A]]) Lens[*S, A] {
|
||||||
return fromOption(lens.MakeLensRefCurried[S, A], defaultValue)
|
return fromOption(lens.MakeLensRefCurried[S, A], defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromIso converts a Lens[S, A] to a LensO[S, A] using an isomorphism.
|
||||||
|
//
|
||||||
|
// This function takes an isomorphism between A and Option[A] and uses it to
|
||||||
|
// transform a regular lens into an optional lens. It's particularly useful when
|
||||||
|
// you have a custom isomorphism that defines how to convert between a value
|
||||||
|
// and its optional representation.
|
||||||
|
//
|
||||||
|
// The isomorphism must satisfy the round-trip laws:
|
||||||
|
// 1. iso.ReverseGet(iso.Get(a)) == a for all a: A
|
||||||
|
// 2. iso.Get(iso.ReverseGet(opt)) == opt for all opt: Option[A]
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The structure type containing the field
|
||||||
|
// - A: The type of the field being focused on
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - iso: An isomorphism between A and Option[A] that defines the conversion
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[S, A] and returns a LensO[S, A]
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Config struct {
|
||||||
|
// timeout int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Create a lens to the timeout field
|
||||||
|
// timeoutLens := lens.MakeLens(
|
||||||
|
// func(c Config) int { return c.timeout },
|
||||||
|
// func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Create an isomorphism that treats 0 as None
|
||||||
|
// zeroAsNone := iso.MakeIso(
|
||||||
|
// func(t int) option.Option[int] {
|
||||||
|
// if t == 0 {
|
||||||
|
// return option.None[int]()
|
||||||
|
// }
|
||||||
|
// return option.Some(t)
|
||||||
|
// },
|
||||||
|
// func(opt option.Option[int]) int {
|
||||||
|
// return option.GetOrElse(func() int { return 0 })(opt)
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Convert to optional lens
|
||||||
|
// optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
//
|
||||||
|
// config := Config{timeout: 0}
|
||||||
|
// opt := optTimeoutLens.Get(config) // None[int]()
|
||||||
|
// updated := optTimeoutLens.Set(option.Some(30))(config) // Config{timeout: 30}
|
||||||
|
//
|
||||||
|
// Common Use Cases:
|
||||||
|
// - Converting between sentinel values (like 0, -1, "") and Option
|
||||||
|
// - Applying custom validation logic when converting to/from Option
|
||||||
|
// - Integrating with existing isomorphisms like FromNillable
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - FromPredicate: For predicate-based optional conversion
|
||||||
|
// - FromNillable: For pointer-based optional conversion
|
||||||
|
// - FromOption: For converting from optional to non-optional with defaults
|
||||||
|
func FromIso[S, A any](iso Iso[A, Option[A]]) func(Lens[S, A]) LensO[S, A] {
|
||||||
|
return LI.Compose[S](iso)
|
||||||
|
}
|
||||||
|
|||||||
481
v2/optics/lens/option/from_test.go
Normal file
481
v2/optics/lens/option/from_test.go
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
EQT "github.com/IBM/fp-go/v2/eq/testing"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
ISO "github.com/IBM/fp-go/v2/optics/iso"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
LT "github.com/IBM/fp-go/v2/optics/lens/testing"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test types
|
||||||
|
type Config struct {
|
||||||
|
timeout int
|
||||||
|
retries int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
maxConnections int
|
||||||
|
bufferSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoBasic tests basic functionality of FromIso
|
||||||
|
func TestFromIsoBasic(t *testing.T) {
|
||||||
|
// Create an isomorphism that treats 0 as None
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a lens to the timeout field
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.timeout },
|
||||||
|
func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Convert to optional lens using FromIso
|
||||||
|
optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
|
||||||
|
t.Run("GetNone", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 0, retries: 3}
|
||||||
|
result := optTimeoutLens.Get(config)
|
||||||
|
assert.True(t, O.IsNone(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetSome", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 30, retries: 3}
|
||||||
|
result := optTimeoutLens.Get(config)
|
||||||
|
assert.True(t, O.IsSome(result))
|
||||||
|
assert.Equal(t, 30, O.GetOrElse(F.Constant(0))(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetNone", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 30, retries: 3}
|
||||||
|
updated := optTimeoutLens.Set(O.None[int]())(config)
|
||||||
|
assert.Equal(t, 0, updated.timeout)
|
||||||
|
assert.Equal(t, 3, updated.retries) // Other fields unchanged
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetSome", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 0, retries: 3}
|
||||||
|
updated := optTimeoutLens.Set(O.Some(60))(config)
|
||||||
|
assert.Equal(t, 60, updated.timeout)
|
||||||
|
assert.Equal(t, 3, updated.retries) // Other fields unchanged
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetPreservesOriginal", func(t *testing.T) {
|
||||||
|
original := Config{timeout: 30, retries: 3}
|
||||||
|
_ = optTimeoutLens.Set(O.Some(60))(original)
|
||||||
|
// Original should be unchanged
|
||||||
|
assert.Equal(t, 30, original.timeout)
|
||||||
|
assert.Equal(t, 3, original.retries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoWithNegativeSentinel tests using -1 as a sentinel value
|
||||||
|
func TestFromIsoWithNegativeSentinel(t *testing.T) {
|
||||||
|
// Create an isomorphism that treats -1 as None
|
||||||
|
negativeOneAsNone := ISO.MakeIso(
|
||||||
|
func(n int) O.Option[int] {
|
||||||
|
if n == -1 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(n)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(-1))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
retriesLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.retries },
|
||||||
|
func(c Config, r int) Config { c.retries = r; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
optRetriesLens := FromIso[Config, int](negativeOneAsNone)(retriesLens)
|
||||||
|
|
||||||
|
t.Run("GetNoneForNegativeOne", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 30, retries: -1}
|
||||||
|
result := optRetriesLens.Get(config)
|
||||||
|
assert.True(t, O.IsNone(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetSomeForZero", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 30, retries: 0}
|
||||||
|
result := optRetriesLens.Get(config)
|
||||||
|
assert.True(t, O.IsSome(result))
|
||||||
|
assert.Equal(t, 0, O.GetOrElse(F.Constant(-1))(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetNoneToNegativeOne", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 30, retries: 5}
|
||||||
|
updated := optRetriesLens.Set(O.None[int]())(config)
|
||||||
|
assert.Equal(t, -1, updated.retries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoLaws verifies that FromIso satisfies lens laws
|
||||||
|
func TestFromIsoLaws(t *testing.T) {
|
||||||
|
// Create an isomorphism
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.timeout },
|
||||||
|
func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
|
||||||
|
eqOptInt := O.Eq(EQT.Eq[int]())
|
||||||
|
eqConfig := EQT.Eq[Config]()
|
||||||
|
|
||||||
|
config := Config{timeout: 30, retries: 3}
|
||||||
|
newValue := O.Some(60)
|
||||||
|
|
||||||
|
// Law 1: GetSet - lens.Set(lens.Get(s))(s) == s
|
||||||
|
t.Run("GetSetLaw", func(t *testing.T) {
|
||||||
|
result := optTimeoutLens.Set(optTimeoutLens.Get(config))(config)
|
||||||
|
assert.True(t, eqConfig.Equals(config, result))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 2: SetGet - lens.Get(lens.Set(a)(s)) == a
|
||||||
|
t.Run("SetGetLaw", func(t *testing.T) {
|
||||||
|
result := optTimeoutLens.Get(optTimeoutLens.Set(newValue)(config))
|
||||||
|
assert.True(t, eqOptInt.Equals(newValue, result))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 3: SetSet - lens.Set(a2)(lens.Set(a1)(s)) == lens.Set(a2)(s)
|
||||||
|
t.Run("SetSetLaw", func(t *testing.T) {
|
||||||
|
a1 := O.Some(60)
|
||||||
|
a2 := O.None[int]()
|
||||||
|
result1 := optTimeoutLens.Set(a2)(optTimeoutLens.Set(a1)(config))
|
||||||
|
result2 := optTimeoutLens.Set(a2)(config)
|
||||||
|
assert.True(t, eqConfig.Equals(result1, result2))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Use the testing helper to verify all laws
|
||||||
|
t.Run("AllLaws", func(t *testing.T) {
|
||||||
|
laws := LT.AssertLaws(t, eqOptInt, eqConfig)(optTimeoutLens)
|
||||||
|
assert.True(t, laws(config, O.Some(100)))
|
||||||
|
assert.True(t, laws(Config{timeout: 0, retries: 5}, O.None[int]()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoComposition tests composing FromIso with other lenses
|
||||||
|
func TestFromIsoComposition(t *testing.T) {
|
||||||
|
type Application struct {
|
||||||
|
config Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Isomorphism for zero as none
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lens to config field
|
||||||
|
configLens := L.MakeLens(
|
||||||
|
func(a Application) Config { return a.config },
|
||||||
|
func(a Application, c Config) Application { a.config = c; return a },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lens to timeout field
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.timeout },
|
||||||
|
func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose: Application -> Config -> timeout (as Option)
|
||||||
|
optTimeoutFromConfig := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
optTimeoutFromApp := F.Pipe1(
|
||||||
|
configLens,
|
||||||
|
L.Compose[Application](optTimeoutFromConfig),
|
||||||
|
)
|
||||||
|
|
||||||
|
app := Application{config: Config{timeout: 0, retries: 3}}
|
||||||
|
|
||||||
|
t.Run("ComposedGet", func(t *testing.T) {
|
||||||
|
result := optTimeoutFromApp.Get(app)
|
||||||
|
assert.True(t, O.IsNone(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ComposedSet", func(t *testing.T) {
|
||||||
|
updated := optTimeoutFromApp.Set(O.Some(45))(app)
|
||||||
|
assert.Equal(t, 45, updated.config.timeout)
|
||||||
|
assert.Equal(t, 3, updated.config.retries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoModify tests using Modify with FromIso-based lenses
|
||||||
|
func TestFromIsoModify(t *testing.T) {
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.timeout },
|
||||||
|
func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
|
||||||
|
t.Run("ModifyNoneToSome", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 0, retries: 3}
|
||||||
|
// Map None to Some(10)
|
||||||
|
modified := L.Modify[Config](O.Map(func(x int) int { return x + 10 }))(optTimeoutLens)(config)
|
||||||
|
// Since it was None, Map doesn't apply, stays None (0)
|
||||||
|
assert.Equal(t, 0, modified.timeout)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ModifySomeValue", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 30, retries: 3}
|
||||||
|
// Double the timeout value
|
||||||
|
modified := L.Modify[Config](O.Map(func(x int) int { return x * 2 }))(optTimeoutLens)(config)
|
||||||
|
assert.Equal(t, 60, modified.timeout)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ModifyWithAlt", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 0, retries: 3}
|
||||||
|
// Use Alt to provide a default
|
||||||
|
modified := L.Modify[Config](func(opt O.Option[int]) O.Option[int] {
|
||||||
|
return O.Alt(F.Constant(O.Some(10)))(opt)
|
||||||
|
})(optTimeoutLens)(config)
|
||||||
|
assert.Equal(t, 10, modified.timeout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoWithStringEmpty tests using empty string as None
|
||||||
|
func TestFromIsoWithStringEmpty(t *testing.T) {
|
||||||
|
type User struct {
|
||||||
|
name string
|
||||||
|
email string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Isomorphism that treats empty string as None
|
||||||
|
emptyAsNone := ISO.MakeIso(
|
||||||
|
func(s string) O.Option[string] {
|
||||||
|
if s == "" {
|
||||||
|
return O.None[string]()
|
||||||
|
}
|
||||||
|
return O.Some(s)
|
||||||
|
},
|
||||||
|
func(opt O.Option[string]) string {
|
||||||
|
return O.GetOrElse(F.Constant(""))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
emailLens := L.MakeLens(
|
||||||
|
func(u User) string { return u.email },
|
||||||
|
func(u User, e string) User { u.email = e; return u },
|
||||||
|
)
|
||||||
|
|
||||||
|
optEmailLens := FromIso[User, string](emptyAsNone)(emailLens)
|
||||||
|
|
||||||
|
t.Run("EmptyStringAsNone", func(t *testing.T) {
|
||||||
|
user := User{name: "Alice", email: ""}
|
||||||
|
result := optEmailLens.Get(user)
|
||||||
|
assert.True(t, O.IsNone(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("NonEmptyStringAsSome", func(t *testing.T) {
|
||||||
|
user := User{name: "Alice", email: "alice@example.com"}
|
||||||
|
result := optEmailLens.Get(user)
|
||||||
|
assert.True(t, O.IsSome(result))
|
||||||
|
assert.Equal(t, "alice@example.com", O.GetOrElse(F.Constant(""))(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetNoneToEmpty", func(t *testing.T) {
|
||||||
|
user := User{name: "Alice", email: "alice@example.com"}
|
||||||
|
updated := optEmailLens.Set(O.None[string]())(user)
|
||||||
|
assert.Equal(t, "", updated.email)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoRoundTrip tests round-trip conversions
|
||||||
|
func TestFromIsoRoundTrip(t *testing.T) {
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
maxConnectionsLens := L.MakeLens(
|
||||||
|
func(s Settings) int { return s.maxConnections },
|
||||||
|
func(s Settings, m int) Settings { s.maxConnections = m; return s },
|
||||||
|
)
|
||||||
|
|
||||||
|
optMaxConnectionsLens := FromIso[Settings, int](zeroAsNone)(maxConnectionsLens)
|
||||||
|
|
||||||
|
t.Run("RoundTripThroughGet", func(t *testing.T) {
|
||||||
|
settings := Settings{maxConnections: 100, bufferSize: 1024}
|
||||||
|
// Get the value, then Set it back
|
||||||
|
opt := optMaxConnectionsLens.Get(settings)
|
||||||
|
restored := optMaxConnectionsLens.Set(opt)(settings)
|
||||||
|
assert.Equal(t, settings, restored)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("RoundTripThroughSet", func(t *testing.T) {
|
||||||
|
settings := Settings{maxConnections: 0, bufferSize: 1024}
|
||||||
|
// Set a new value, then Get it
|
||||||
|
newOpt := O.Some(200)
|
||||||
|
updated := optMaxConnectionsLens.Set(newOpt)(settings)
|
||||||
|
retrieved := optMaxConnectionsLens.Get(updated)
|
||||||
|
assert.True(t, O.Eq(EQT.Eq[int]()).Equals(newOpt, retrieved))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("RoundTripWithNone", func(t *testing.T) {
|
||||||
|
settings := Settings{maxConnections: 100, bufferSize: 1024}
|
||||||
|
// Set None, then get it back
|
||||||
|
updated := optMaxConnectionsLens.Set(O.None[int]())(settings)
|
||||||
|
retrieved := optMaxConnectionsLens.Get(updated)
|
||||||
|
assert.True(t, O.IsNone(retrieved))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoChaining tests chaining multiple FromIso transformations
|
||||||
|
func TestFromIsoChaining(t *testing.T) {
|
||||||
|
// Create two different isomorphisms
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.timeout },
|
||||||
|
func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
|
||||||
|
config := Config{timeout: 30, retries: 3}
|
||||||
|
|
||||||
|
t.Run("ChainedOperations", func(t *testing.T) {
|
||||||
|
// Chain multiple operations
|
||||||
|
result := F.Pipe2(
|
||||||
|
config,
|
||||||
|
optTimeoutLens.Set(O.Some(60)),
|
||||||
|
optTimeoutLens.Set(O.None[int]()),
|
||||||
|
)
|
||||||
|
assert.Equal(t, 0, result.timeout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFromIsoMultipleFields tests using FromIso on multiple fields
|
||||||
|
func TestFromIsoMultipleFields(t *testing.T) {
|
||||||
|
zeroAsNone := ISO.MakeIso(
|
||||||
|
func(t int) O.Option[int] {
|
||||||
|
if t == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Some(t)
|
||||||
|
},
|
||||||
|
func(opt O.Option[int]) int {
|
||||||
|
return O.GetOrElse(F.Constant(0))(opt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.timeout },
|
||||||
|
func(c Config, t int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
retriesLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.retries },
|
||||||
|
func(c Config, r int) Config { c.retries = r; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
|
||||||
|
optRetriesLens := FromIso[Config, int](zeroAsNone)(retriesLens)
|
||||||
|
|
||||||
|
t.Run("IndependentFields", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 0, retries: 5}
|
||||||
|
|
||||||
|
// Get both fields
|
||||||
|
timeoutOpt := optTimeoutLens.Get(config)
|
||||||
|
retriesOpt := optRetriesLens.Get(config)
|
||||||
|
|
||||||
|
assert.True(t, O.IsNone(timeoutOpt))
|
||||||
|
assert.True(t, O.IsSome(retriesOpt))
|
||||||
|
assert.Equal(t, 5, O.GetOrElse(F.Constant(0))(retriesOpt))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SetBothFields", func(t *testing.T) {
|
||||||
|
config := Config{timeout: 0, retries: 0}
|
||||||
|
|
||||||
|
// Set both fields
|
||||||
|
updated := F.Pipe2(
|
||||||
|
config,
|
||||||
|
optTimeoutLens.Set(O.Some(30)),
|
||||||
|
optRetriesLens.Set(O.Some(3)),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, 30, updated.timeout)
|
||||||
|
assert.Equal(t, 3, updated.retries)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Made with Bob
|
||||||
@@ -17,6 +17,7 @@ package option
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/endomorphism"
|
"github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/iso"
|
||||||
"github.com/IBM/fp-go/v2/optics/lens"
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
"github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
@@ -91,4 +92,6 @@ type (
|
|||||||
// optLens := lens.FromNillableRef(timeoutLens)
|
// optLens := lens.FromNillableRef(timeoutLens)
|
||||||
// // optLens is a LensO[*Config, *int]
|
// // optLens is a LensO[*Config, *int]
|
||||||
LensO[S, A any] = Lens[S, Option[A]]
|
LensO[S, A any] = Lens[S, Option[A]]
|
||||||
|
|
||||||
|
Iso[S, A any] = iso.Iso[S, A]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -80,4 +80,7 @@ type (
|
|||||||
// with the focused value updated to a. The original structure is never modified.
|
// with the focused value updated to a. The original structure is never modified.
|
||||||
Set func(a A) Endomorphism[S]
|
Set func(a A) Endomorphism[S]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kleisli[S, A, B any] = func(A) Lens[S, B]
|
||||||
|
Operator[S, A, B any] = Kleisli[S, Lens[S, A], B]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -196,3 +197,147 @@ func TestPersonRefLensesIdempotent(t *testing.T) {
|
|||||||
assert.Equal(t, "bob@example.com", differentEmail.Email)
|
assert.Equal(t, "bob@example.com", differentEmail.Email)
|
||||||
assert.Equal(t, "alice@example.com", person.Email, "Original should be unchanged")
|
assert.Equal(t, "alice@example.com", person.Email, "Original should be unchanged")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPersonRefLensesOptionalIdempotent(t *testing.T) {
|
||||||
|
// Test that setting an optional field to the same value returns the identical pointer
|
||||||
|
// This is important for performance and correctness in functional programming
|
||||||
|
|
||||||
|
// Test with Phone field set to a value
|
||||||
|
phoneValue := "555-1234"
|
||||||
|
person := &Person{
|
||||||
|
Name: "Alice",
|
||||||
|
Age: 30,
|
||||||
|
Email: "alice@example.com",
|
||||||
|
Phone: &phoneValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
refLenses := MakePersonRefLenses()
|
||||||
|
|
||||||
|
// Test that setting Phone to the same value returns the same pointer
|
||||||
|
samePhone := refLenses.Phone.Set(O.Some(&phoneValue))(person)
|
||||||
|
assert.Same(t, person, samePhone, "Setting Phone to same value should return identical pointer")
|
||||||
|
|
||||||
|
// Test with Phone field set to nil
|
||||||
|
personNoPhone := &Person{
|
||||||
|
Name: "Bob",
|
||||||
|
Age: 25,
|
||||||
|
Email: "bob@example.com",
|
||||||
|
Phone: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting Phone to None when it's already nil should return same pointer
|
||||||
|
sameNilPhone := refLenses.Phone.Set(O.None[*string]())(personNoPhone)
|
||||||
|
assert.Same(t, personNoPhone, sameNilPhone, "Setting Phone to None when already nil should return identical pointer")
|
||||||
|
|
||||||
|
// Test that setting to a different value creates a new pointer
|
||||||
|
newPhoneValue := "555-5678"
|
||||||
|
differentPhone := refLenses.Phone.Set(O.Some(&newPhoneValue))(person)
|
||||||
|
assert.NotSame(t, person, differentPhone, "Setting Phone to different value should return new pointer")
|
||||||
|
assert.Equal(t, &newPhoneValue, differentPhone.Phone)
|
||||||
|
assert.Equal(t, &phoneValue, person.Phone, "Original should be unchanged")
|
||||||
|
|
||||||
|
// Test setting from nil to Some creates new pointer
|
||||||
|
somePhone := refLenses.Phone.Set(O.Some(&phoneValue))(personNoPhone)
|
||||||
|
assert.NotSame(t, personNoPhone, somePhone, "Setting Phone from nil to Some should return new pointer")
|
||||||
|
assert.Equal(t, &phoneValue, somePhone.Phone)
|
||||||
|
assert.Nil(t, personNoPhone.Phone, "Original should be unchanged")
|
||||||
|
|
||||||
|
// Test setting from Some to None creates new pointer
|
||||||
|
nonePhone := refLenses.Phone.Set(O.None[*string]())(person)
|
||||||
|
assert.NotSame(t, person, nonePhone, "Setting Phone from Some to None should return new pointer")
|
||||||
|
assert.Nil(t, nonePhone.Phone)
|
||||||
|
assert.Equal(t, &phoneValue, person.Phone, "Original should be unchanged")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddressRefLensesOptionalIdempotent(t *testing.T) {
|
||||||
|
// Test Address.State optional field idempotency
|
||||||
|
|
||||||
|
stateValue := "California"
|
||||||
|
address := &Address{
|
||||||
|
Street: "123 Main St",
|
||||||
|
City: "Los Angeles",
|
||||||
|
ZipCode: "90001",
|
||||||
|
Country: "USA",
|
||||||
|
State: &stateValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
refLenses := MakeAddressRefLenses()
|
||||||
|
|
||||||
|
// Test that setting State to the same value returns the same pointer
|
||||||
|
sameState := refLenses.State.Set(O.Some(&stateValue))(address)
|
||||||
|
assert.Same(t, address, sameState, "Setting State to same value should return identical pointer")
|
||||||
|
|
||||||
|
// Test with State field set to nil
|
||||||
|
addressNoState := &Address{
|
||||||
|
Street: "456 Oak Ave",
|
||||||
|
City: "Boston",
|
||||||
|
ZipCode: "02101",
|
||||||
|
Country: "USA",
|
||||||
|
State: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting State to None when it's already nil should return same pointer
|
||||||
|
sameNilState := refLenses.State.Set(O.None[*string]())(addressNoState)
|
||||||
|
assert.Same(t, addressNoState, sameNilState, "Setting State to None when already nil should return identical pointer")
|
||||||
|
|
||||||
|
// Test that setting to a different value creates a new pointer
|
||||||
|
newStateValue := "New York"
|
||||||
|
differentState := refLenses.State.Set(O.Some(&newStateValue))(address)
|
||||||
|
assert.NotSame(t, address, differentState, "Setting State to different value should return new pointer")
|
||||||
|
assert.Equal(t, &newStateValue, differentState.State)
|
||||||
|
assert.Equal(t, &stateValue, address.State, "Original should be unchanged")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompanyRefLensesOptionalIdempotent(t *testing.T) {
|
||||||
|
// Test Company.Website optional field idempotency
|
||||||
|
|
||||||
|
websiteValue := "https://example.com"
|
||||||
|
company := &Company{
|
||||||
|
Name: "Tech Inc",
|
||||||
|
Address: Address{
|
||||||
|
Street: "789 Tech Blvd",
|
||||||
|
City: "San Francisco",
|
||||||
|
ZipCode: "94102",
|
||||||
|
Country: "USA",
|
||||||
|
},
|
||||||
|
CEO: Person{
|
||||||
|
Name: "Jane Doe",
|
||||||
|
Age: 45,
|
||||||
|
Email: "jane@techinc.com",
|
||||||
|
},
|
||||||
|
Website: &websiteValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
refLenses := MakeCompanyRefLenses()
|
||||||
|
|
||||||
|
// Test that setting Website to the same value returns the same pointer
|
||||||
|
sameWebsite := refLenses.Website.Set(O.Some(&websiteValue))(company)
|
||||||
|
assert.Same(t, company, sameWebsite, "Setting Website to same value should return identical pointer")
|
||||||
|
|
||||||
|
// Test with Website field set to nil
|
||||||
|
companyNoWebsite := &Company{
|
||||||
|
Name: "Startup LLC",
|
||||||
|
Address: Address{
|
||||||
|
Street: "101 Innovation Way",
|
||||||
|
City: "Austin",
|
||||||
|
ZipCode: "78701",
|
||||||
|
Country: "USA",
|
||||||
|
},
|
||||||
|
CEO: Person{
|
||||||
|
Name: "John Smith",
|
||||||
|
Age: 35,
|
||||||
|
Email: "john@startup.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting Website to None when it's already nil should return same pointer
|
||||||
|
sameNilWebsite := refLenses.Website.Set(O.None[*string]())(companyNoWebsite)
|
||||||
|
assert.Same(t, companyNoWebsite, sameNilWebsite, "Setting Website to None when already nil should return identical pointer")
|
||||||
|
|
||||||
|
// Test that setting to a different value creates a new pointer
|
||||||
|
newWebsiteValue := "https://newsite.com"
|
||||||
|
differentWebsite := refLenses.Website.Set(O.Some(&newWebsiteValue))(company)
|
||||||
|
assert.NotSame(t, company, differentWebsite, "Setting Website to different value should return new pointer")
|
||||||
|
assert.Equal(t, &newWebsiteValue, differentWebsite.Website)
|
||||||
|
assert.Equal(t, &websiteValue, company.Website, "Original should be unchanged")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package lens
|
|||||||
|
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2025-11-12 17:16:40.1431921 +0100 CET m=+0.003694701
|
// 2025-11-12 18:15:07.69943 +0100 CET m=+0.005345401
|
||||||
|
|
||||||
import (
|
import (
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
@@ -53,7 +53,6 @@ func MakePersonLenses() PersonLenses {
|
|||||||
|
|
||||||
// MakePersonRefLenses creates a new PersonRefLenses with lenses for all fields
|
// MakePersonRefLenses creates a new PersonRefLenses with lenses for all fields
|
||||||
func MakePersonRefLenses() PersonRefLenses {
|
func MakePersonRefLenses() PersonRefLenses {
|
||||||
isoPhone := I.FromZero[*string]()
|
|
||||||
return PersonRefLenses{
|
return PersonRefLenses{
|
||||||
Name: L.MakeLensStrict(
|
Name: L.MakeLensStrict(
|
||||||
func(s *Person) string { return s.Name },
|
func(s *Person) string { return s.Name },
|
||||||
@@ -67,10 +66,10 @@ func MakePersonRefLenses() PersonRefLenses {
|
|||||||
func(s *Person) string { return s.Email },
|
func(s *Person) string { return s.Email },
|
||||||
func(s *Person, v string) *Person { s.Email = v; return s },
|
func(s *Person, v string) *Person { s.Email = v; return s },
|
||||||
),
|
),
|
||||||
Phone: L.MakeLensRef(
|
Phone: LO.FromIso[*Person](I.FromZero[*string]())(L.MakeLensStrict(
|
||||||
func(s *Person) O.Option[*string] { return isoPhone.Get(s.Phone) },
|
func(s *Person) *string { return s.Phone },
|
||||||
func(s *Person, v O.Option[*string]) *Person { s.Phone = isoPhone.ReverseGet(v); return s },
|
func(s *Person, v *string) *Person { s.Phone = v; return s },
|
||||||
),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +120,6 @@ func MakeAddressLenses() AddressLenses {
|
|||||||
|
|
||||||
// MakeAddressRefLenses creates a new AddressRefLenses with lenses for all fields
|
// MakeAddressRefLenses creates a new AddressRefLenses with lenses for all fields
|
||||||
func MakeAddressRefLenses() AddressRefLenses {
|
func MakeAddressRefLenses() AddressRefLenses {
|
||||||
isoState := I.FromZero[*string]()
|
|
||||||
return AddressRefLenses{
|
return AddressRefLenses{
|
||||||
Street: L.MakeLensStrict(
|
Street: L.MakeLensStrict(
|
||||||
func(s *Address) string { return s.Street },
|
func(s *Address) string { return s.Street },
|
||||||
@@ -139,10 +137,10 @@ func MakeAddressRefLenses() AddressRefLenses {
|
|||||||
func(s *Address) string { return s.Country },
|
func(s *Address) string { return s.Country },
|
||||||
func(s *Address, v string) *Address { s.Country = v; return s },
|
func(s *Address, v string) *Address { s.Country = v; return s },
|
||||||
),
|
),
|
||||||
State: L.MakeLensRef(
|
State: LO.FromIso[*Address](I.FromZero[*string]())(L.MakeLensStrict(
|
||||||
func(s *Address) O.Option[*string] { return isoState.Get(s.State) },
|
func(s *Address) *string { return s.State },
|
||||||
func(s *Address, v O.Option[*string]) *Address { s.State = isoState.ReverseGet(v); return s },
|
func(s *Address, v *string) *Address { s.State = v; return s },
|
||||||
),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +185,6 @@ func MakeCompanyLenses() CompanyLenses {
|
|||||||
|
|
||||||
// MakeCompanyRefLenses creates a new CompanyRefLenses with lenses for all fields
|
// MakeCompanyRefLenses creates a new CompanyRefLenses with lenses for all fields
|
||||||
func MakeCompanyRefLenses() CompanyRefLenses {
|
func MakeCompanyRefLenses() CompanyRefLenses {
|
||||||
isoWebsite := I.FromZero[*string]()
|
|
||||||
return CompanyRefLenses{
|
return CompanyRefLenses{
|
||||||
Name: L.MakeLensStrict(
|
Name: L.MakeLensStrict(
|
||||||
func(s *Company) string { return s.Name },
|
func(s *Company) string { return s.Name },
|
||||||
@@ -201,10 +198,10 @@ func MakeCompanyRefLenses() CompanyRefLenses {
|
|||||||
func(s *Company) Person { return s.CEO },
|
func(s *Company) Person { return s.CEO },
|
||||||
func(s *Company, v Person) *Company { s.CEO = v; return s },
|
func(s *Company, v Person) *Company { s.CEO = v; return s },
|
||||||
),
|
),
|
||||||
Website: L.MakeLensRef(
|
Website: LO.FromIso[*Company](I.FromZero[*string]())(L.MakeLensStrict(
|
||||||
func(s *Company) O.Option[*string] { return isoWebsite.Get(s.Website) },
|
func(s *Company) *string { return s.Website },
|
||||||
func(s *Company, v O.Option[*string]) *Company { s.Website = isoWebsite.ReverseGet(v); return s },
|
func(s *Company, v *string) *Company { s.Website = v; return s },
|
||||||
),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,15 +234,14 @@ func MakeCheckOptionLenses() CheckOptionLenses {
|
|||||||
|
|
||||||
// MakeCheckOptionRefLenses creates a new CheckOptionRefLenses with lenses for all fields
|
// MakeCheckOptionRefLenses creates a new CheckOptionRefLenses with lenses for all fields
|
||||||
func MakeCheckOptionRefLenses() CheckOptionRefLenses {
|
func MakeCheckOptionRefLenses() CheckOptionRefLenses {
|
||||||
isoValue := I.FromZero[string]()
|
|
||||||
return CheckOptionRefLenses{
|
return CheckOptionRefLenses{
|
||||||
Name: L.MakeLensRef(
|
Name: L.MakeLensRef(
|
||||||
func(s *CheckOption) option.Option[string] { return s.Name },
|
func(s *CheckOption) option.Option[string] { return s.Name },
|
||||||
func(s *CheckOption, v option.Option[string]) *CheckOption { s.Name = v; return s },
|
func(s *CheckOption, v option.Option[string]) *CheckOption { s.Name = v; return s },
|
||||||
),
|
),
|
||||||
Value: L.MakeLensRef(
|
Value: LO.FromIso[*CheckOption](I.FromZero[string]())(L.MakeLensStrict(
|
||||||
func(s *CheckOption) O.Option[string] { return isoValue.Get(s.Value) },
|
func(s *CheckOption) string { return s.Value },
|
||||||
func(s *CheckOption, v O.Option[string]) *CheckOption { s.Value = isoValue.ReverseGet(v); return s },
|
func(s *CheckOption, v string) *CheckOption { s.Value = v; return s },
|
||||||
),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user