mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
fix: examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -76,15 +76,25 @@ type templateData struct {
|
|||||||
const lensStructTemplate = `
|
const lensStructTemplate = `
|
||||||
// {{.Name}}Lenses provides lenses for accessing fields of {{.Name}}
|
// {{.Name}}Lenses provides lenses for accessing fields of {{.Name}}
|
||||||
type {{.Name}}Lenses struct {
|
type {{.Name}}Lenses struct {
|
||||||
|
// mandatory fields
|
||||||
{{- range .Fields}}
|
{{- range .Fields}}
|
||||||
{{.Name}} {{if .IsOptional}}LO.LensO[{{$.Name}}, {{.TypeName}}]{{else}}L.Lens[{{$.Name}}, {{.TypeName}}]{{end}}
|
{{.Name}} L.Lens[{{$.Name}}, {{.TypeName}}]
|
||||||
|
{{- end}}
|
||||||
|
// optional fields
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{.Name}}O LO.LensO[{{$.Name}}, {{.TypeName}}]
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Name}}RefLenses provides lenses for accessing fields of {{.Name}} via a reference to {{.Name}}
|
// {{.Name}}RefLenses provides lenses for accessing fields of {{.Name}} via a reference to {{.Name}}
|
||||||
type {{.Name}}RefLenses struct {
|
type {{.Name}}RefLenses struct {
|
||||||
|
// mandatory fields
|
||||||
{{- range .Fields}}
|
{{- range .Fields}}
|
||||||
{{.Name}} {{if .IsOptional}}LO.LensO[*{{$.Name}}, {{.TypeName}}]{{else}}L.Lens[*{{$.Name}}, {{.TypeName}}]{{end}}
|
{{.Name}} L.Lens[*{{$.Name}}, {{.TypeName}}]
|
||||||
|
{{- end}}
|
||||||
|
// optional fields
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{.Name}}O LO.LensO[*{{$.Name}}, {{.TypeName}}]
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@@ -92,57 +102,57 @@ type {{.Name}}RefLenses struct {
|
|||||||
const lensConstructorTemplate = `
|
const lensConstructorTemplate = `
|
||||||
// Make{{.Name}}Lenses creates a new {{.Name}}Lenses with lenses for all fields
|
// Make{{.Name}}Lenses creates a new {{.Name}}Lenses with lenses for all fields
|
||||||
func Make{{.Name}}Lenses() {{.Name}}Lenses {
|
func Make{{.Name}}Lenses() {{.Name}}Lenses {
|
||||||
|
// mandatory lenses
|
||||||
{{- range .Fields}}
|
{{- range .Fields}}
|
||||||
{{- if .IsOptional}}
|
lens{{.Name}} := L.MakeLens(
|
||||||
iso{{.Name}} := I.FromZero[{{.TypeName}}]()
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
return {{.Name}}Lenses{
|
|
||||||
{{- range .Fields}}
|
|
||||||
{{- if .IsOptional}}
|
|
||||||
{{.Name}}: L.MakeLens(
|
|
||||||
func(s {{$.Name}}) O.Option[{{.TypeName}}] { return iso{{.Name}}.Get(s.{{.Name}}) },
|
|
||||||
func(s {{$.Name}}, v O.Option[{{.TypeName}}]) {{$.Name}} { s.{{.Name}} = iso{{.Name}}.ReverseGet(v); return s },
|
|
||||||
),
|
|
||||||
{{- else}}
|
|
||||||
{{.Name}}: L.MakeLens(
|
|
||||||
func(s {{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
func(s {{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
||||||
func(s {{$.Name}}, v {{.TypeName}}) {{$.Name}} { s.{{.Name}} = v; return s },
|
func(s {{$.Name}}, v {{.TypeName}}) {{$.Name}} { s.{{.Name}} = v; return s },
|
||||||
),
|
)
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
// optional lenses
|
||||||
|
{{- range .Fields}}
|
||||||
|
lens{{.Name}}O := LO.FromIso[{{$.Name}}](IO.FromZero[{{.TypeName}}]())(lens{{.Name}})
|
||||||
|
{{- end}}
|
||||||
|
return {{.Name}}Lenses{
|
||||||
|
// mandatory lenses
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{.Name}}: lens{{.Name}},
|
||||||
|
{{- end}}
|
||||||
|
// optional lenses
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{.Name}}O: lens{{.Name}}O,
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
return {{.Name}}RefLenses{
|
// mandatory lenses
|
||||||
{{- range .Fields}}
|
{{- range .Fields}}
|
||||||
{{- if .IsOptional}}
|
|
||||||
{{- if .IsComparable}}
|
{{- if .IsComparable}}
|
||||||
{{.Name}}: LO.FromIso[*{{$.Name}}](I.FromZero[{{.TypeName}}]())(L.MakeLensStrict(
|
lens{{.Name}} := L.MakeLensStrict(
|
||||||
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
||||||
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
||||||
)),
|
)
|
||||||
{{- else}}
|
{{- else}}
|
||||||
{{.Name}}: LO.FromIso[*{{$.Name}}](I.FromZero[{{.TypeName}}]())(L.MakeLensRef(
|
lens{{.Name}} := L.MakeLensRef(
|
||||||
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
||||||
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
||||||
)),
|
)
|
||||||
{{- end}}
|
|
||||||
{{- else}}
|
|
||||||
{{- if .IsComparable}}
|
|
||||||
{{.Name}}: L.MakeLensStrict(
|
|
||||||
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
|
||||||
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
|
||||||
),
|
|
||||||
{{- else}}
|
|
||||||
{{.Name}}: L.MakeLensRef(
|
|
||||||
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
|
|
||||||
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
|
|
||||||
),
|
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
// optional lenses
|
||||||
|
{{- range .Fields}}
|
||||||
|
lens{{.Name}}O := LO.FromIso[*{{$.Name}}](IO.FromZero[{{.TypeName}}]())(lens{{.Name}})
|
||||||
|
{{- end}}
|
||||||
|
return {{.Name}}RefLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{.Name}}: lens{{.Name}},
|
||||||
|
{{- end}}
|
||||||
|
// optional lenses
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{.Name}}O: lens{{.Name}}O,
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,6 +372,7 @@ func isComparableType(expr ast.Expr) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// For other generic types, conservatively assume not comparable
|
// For other generic types, conservatively assume not comparable
|
||||||
|
log.Printf("Not comparable type: %v\n", t)
|
||||||
return false
|
return false
|
||||||
case *ast.ChanType:
|
case *ast.ChanType:
|
||||||
// Channel types are comparable
|
// Channel types are comparable
|
||||||
@@ -463,6 +474,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
|
||||||
isComparable = isComparableType(field.Type)
|
isComparable = isComparableType(field.Type)
|
||||||
|
// log.Printf("field %s, type: %v, isComparable: %b\n", name, field.Type, isComparable)
|
||||||
|
|
||||||
// Extract imports from this field's type
|
// Extract imports from this field's type
|
||||||
fieldImports := make(map[string]string)
|
fieldImports := make(map[string]string)
|
||||||
@@ -590,8 +602,8 @@ func generateLensHelpers(dir, filename string, verbose bool) error {
|
|||||||
// Standard fp-go imports always needed
|
// Standard fp-go imports always needed
|
||||||
f.WriteString("\tL \"github.com/IBM/fp-go/v2/optics/lens\"\n")
|
f.WriteString("\tL \"github.com/IBM/fp-go/v2/optics/lens\"\n")
|
||||||
f.WriteString("\tLO \"github.com/IBM/fp-go/v2/optics/lens/option\"\n")
|
f.WriteString("\tLO \"github.com/IBM/fp-go/v2/optics/lens/option\"\n")
|
||||||
f.WriteString("\tO \"github.com/IBM/fp-go/v2/option\"\n")
|
// f.WriteString("\tO \"github.com/IBM/fp-go/v2/option\"\n")
|
||||||
f.WriteString("\tI \"github.com/IBM/fp-go/v2/optics/iso/option\"\n")
|
f.WriteString("\tIO \"github.com/IBM/fp-go/v2/optics/iso/option\"\n")
|
||||||
|
|
||||||
// Add additional imports collected from field types
|
// Add additional imports collected from field types
|
||||||
for importPath, alias := range allImports {
|
for importPath, alias := range allImports {
|
||||||
|
|||||||
@@ -514,15 +514,15 @@ func TestLensRefTemplatesWithComparable(t *testing.T) {
|
|||||||
assert.Contains(t, constructorStr, "func MakeTestStructRefLenses() TestStructRefLenses")
|
assert.Contains(t, constructorStr, "func MakeTestStructRefLenses() TestStructRefLenses")
|
||||||
|
|
||||||
// Name field - comparable, should use MakeLensStrict
|
// Name field - comparable, should use MakeLensStrict
|
||||||
assert.Contains(t, constructorStr, "Name: L.MakeLensStrict(",
|
assert.Contains(t, constructorStr, "lensName := L.MakeLensStrict(",
|
||||||
"comparable field Name should use MakeLensStrict in RefLenses")
|
"comparable field Name should use MakeLensStrict in RefLenses")
|
||||||
|
|
||||||
// Age field - comparable, should use MakeLensStrict
|
// Age field - comparable, should use MakeLensStrict
|
||||||
assert.Contains(t, constructorStr, "Age: L.MakeLensStrict(",
|
assert.Contains(t, constructorStr, "lensAge := L.MakeLensStrict(",
|
||||||
"comparable field Age should use MakeLensStrict in RefLenses")
|
"comparable field Age should use MakeLensStrict in RefLenses")
|
||||||
|
|
||||||
// Data field - not comparable, should use MakeLensRef
|
// Data field - not comparable, should use MakeLensRef
|
||||||
assert.Contains(t, constructorStr, "Data: L.MakeLensRef(",
|
assert.Contains(t, constructorStr, "lensData := L.MakeLensRef(",
|
||||||
"non-comparable field Data should use MakeLensRef in RefLenses")
|
"non-comparable field Data should use MakeLensRef in RefLenses")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -573,12 +573,12 @@ type TestStruct struct {
|
|||||||
"non-comparable fields should use MakeLensRef in RefLenses")
|
"non-comparable fields should use MakeLensRef in RefLenses")
|
||||||
|
|
||||||
// Verify the pattern appears for Name field (comparable)
|
// Verify the pattern appears for Name field (comparable)
|
||||||
namePattern := "Name: L.MakeLensStrict("
|
namePattern := "lensName := L.MakeLensStrict("
|
||||||
assert.Contains(t, contentStr, namePattern,
|
assert.Contains(t, contentStr, namePattern,
|
||||||
"Name field should use MakeLensStrict")
|
"Name field should use MakeLensStrict")
|
||||||
|
|
||||||
// Verify the pattern appears for Data field (not comparable)
|
// Verify the pattern appears for Data field (not comparable)
|
||||||
dataPattern := "Data: L.MakeLensRef("
|
dataPattern := "lensData := L.MakeLensRef("
|
||||||
assert.Contains(t, contentStr, dataPattern,
|
assert.Contains(t, contentStr, dataPattern,
|
||||||
"Data field should use MakeLensRef")
|
"Data field should use MakeLensRef")
|
||||||
}
|
}
|
||||||
@@ -619,11 +619,11 @@ type TestStruct struct {
|
|||||||
// Check for expected content
|
// Check for expected content
|
||||||
assert.Contains(t, contentStr, "package testpkg")
|
assert.Contains(t, contentStr, "package testpkg")
|
||||||
assert.Contains(t, contentStr, "Code generated by go generate")
|
assert.Contains(t, contentStr, "Code generated by go generate")
|
||||||
assert.Contains(t, contentStr, "TestStructLens")
|
assert.Contains(t, contentStr, "TestStructLenses")
|
||||||
assert.Contains(t, contentStr, "MakeTestStructLens")
|
assert.Contains(t, contentStr, "MakeTestStructLenses")
|
||||||
assert.Contains(t, contentStr, "L.Lens[TestStruct, string]")
|
assert.Contains(t, contentStr, "L.Lens[TestStruct, string]")
|
||||||
assert.Contains(t, contentStr, "LO.LensO[TestStruct, *int]")
|
assert.Contains(t, contentStr, "LO.LensO[TestStruct, *int]")
|
||||||
assert.Contains(t, contentStr, "I.FromZero")
|
assert.Contains(t, contentStr, "IO.FromZero")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateLensHelpersNoAnnotations(t *testing.T) {
|
func TestGenerateLensHelpersNoAnnotations(t *testing.T) {
|
||||||
@@ -670,7 +670,9 @@ func TestLensTemplates(t *testing.T) {
|
|||||||
structStr := structBuf.String()
|
structStr := structBuf.String()
|
||||||
assert.Contains(t, structStr, "type TestStructLenses struct")
|
assert.Contains(t, structStr, "type TestStructLenses struct")
|
||||||
assert.Contains(t, structStr, "Name L.Lens[TestStruct, string]")
|
assert.Contains(t, structStr, "Name L.Lens[TestStruct, string]")
|
||||||
assert.Contains(t, structStr, "Value LO.LensO[TestStruct, *int]")
|
assert.Contains(t, structStr, "NameO LO.LensO[TestStruct, string]")
|
||||||
|
assert.Contains(t, structStr, "Value L.Lens[TestStruct, *int]")
|
||||||
|
assert.Contains(t, structStr, "ValueO LO.LensO[TestStruct, *int]")
|
||||||
|
|
||||||
// Test constructor template
|
// Test constructor template
|
||||||
var constructorBuf bytes.Buffer
|
var constructorBuf bytes.Buffer
|
||||||
@@ -680,9 +682,11 @@ func TestLensTemplates(t *testing.T) {
|
|||||||
constructorStr := constructorBuf.String()
|
constructorStr := constructorBuf.String()
|
||||||
assert.Contains(t, constructorStr, "func MakeTestStructLenses() TestStructLenses")
|
assert.Contains(t, constructorStr, "func MakeTestStructLenses() TestStructLenses")
|
||||||
assert.Contains(t, constructorStr, "return TestStructLenses{")
|
assert.Contains(t, constructorStr, "return TestStructLenses{")
|
||||||
assert.Contains(t, constructorStr, "Name: L.MakeLens(")
|
assert.Contains(t, constructorStr, "Name: lensName,")
|
||||||
assert.Contains(t, constructorStr, "Value: L.MakeLens(")
|
assert.Contains(t, constructorStr, "NameO: lensNameO,")
|
||||||
assert.Contains(t, constructorStr, "I.FromZero")
|
assert.Contains(t, constructorStr, "Value: lensValue,")
|
||||||
|
assert.Contains(t, constructorStr, "ValueO: lensValueO,")
|
||||||
|
assert.Contains(t, constructorStr, "IO.FromZero")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLensTemplatesWithOmitEmpty(t *testing.T) {
|
func TestLensTemplatesWithOmitEmpty(t *testing.T) {
|
||||||
@@ -704,9 +708,13 @@ func TestLensTemplatesWithOmitEmpty(t *testing.T) {
|
|||||||
structStr := structBuf.String()
|
structStr := structBuf.String()
|
||||||
assert.Contains(t, structStr, "type ConfigStructLenses struct")
|
assert.Contains(t, structStr, "type ConfigStructLenses struct")
|
||||||
assert.Contains(t, structStr, "Name L.Lens[ConfigStruct, string]")
|
assert.Contains(t, structStr, "Name L.Lens[ConfigStruct, string]")
|
||||||
assert.Contains(t, structStr, "Value LO.LensO[ConfigStruct, string]", "non-pointer with omitempty should use LensO")
|
assert.Contains(t, structStr, "NameO LO.LensO[ConfigStruct, string]")
|
||||||
assert.Contains(t, structStr, "Count LO.LensO[ConfigStruct, int]", "non-pointer with omitempty should use LensO")
|
assert.Contains(t, structStr, "Value L.Lens[ConfigStruct, string]")
|
||||||
assert.Contains(t, structStr, "Pointer LO.LensO[ConfigStruct, *string]")
|
assert.Contains(t, structStr, "ValueO LO.LensO[ConfigStruct, string]", "non-pointer with omitempty should have optional lens")
|
||||||
|
assert.Contains(t, structStr, "Count L.Lens[ConfigStruct, int]")
|
||||||
|
assert.Contains(t, structStr, "CountO LO.LensO[ConfigStruct, int]", "non-pointer with omitempty should have optional lens")
|
||||||
|
assert.Contains(t, structStr, "Pointer L.Lens[ConfigStruct, *string]")
|
||||||
|
assert.Contains(t, structStr, "PointerO LO.LensO[ConfigStruct, *string]")
|
||||||
|
|
||||||
// Test constructor template
|
// Test constructor template
|
||||||
var constructorBuf bytes.Buffer
|
var constructorBuf bytes.Buffer
|
||||||
@@ -715,9 +723,9 @@ func TestLensTemplatesWithOmitEmpty(t *testing.T) {
|
|||||||
|
|
||||||
constructorStr := constructorBuf.String()
|
constructorStr := constructorBuf.String()
|
||||||
assert.Contains(t, constructorStr, "func MakeConfigStructLenses() ConfigStructLenses")
|
assert.Contains(t, constructorStr, "func MakeConfigStructLenses() ConfigStructLenses")
|
||||||
assert.Contains(t, constructorStr, "isoValue := I.FromZero[string]()")
|
assert.Contains(t, constructorStr, "IO.FromZero[string]()")
|
||||||
assert.Contains(t, constructorStr, "isoCount := I.FromZero[int]()")
|
assert.Contains(t, constructorStr, "IO.FromZero[int]()")
|
||||||
assert.Contains(t, constructorStr, "isoPointer := I.FromZero[*string]()")
|
assert.Contains(t, constructorStr, "IO.FromZero[*string]()")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLensCommandFlags(t *testing.T) {
|
func TestLensCommandFlags(t *testing.T) {
|
||||||
@@ -726,7 +734,7 @@ func TestLensCommandFlags(t *testing.T) {
|
|||||||
assert.Equal(t, "lens", cmd.Name)
|
assert.Equal(t, "lens", cmd.Name)
|
||||||
assert.Equal(t, "generate lens code for annotated structs", cmd.Usage)
|
assert.Equal(t, "generate lens code for annotated structs", cmd.Usage)
|
||||||
assert.Contains(t, strings.ToLower(cmd.Description), "fp-go:lens")
|
assert.Contains(t, strings.ToLower(cmd.Description), "fp-go:lens")
|
||||||
assert.Contains(t, strings.ToLower(cmd.Description), "lenso")
|
assert.Contains(t, strings.ToLower(cmd.Description), "lenso", "Description should mention LensO for optional lenses")
|
||||||
|
|
||||||
// Check flags
|
// Check flags
|
||||||
assert.Len(t, cmd.Flags, 3)
|
assert.Len(t, cmd.Flags, 3)
|
||||||
|
|||||||
@@ -1,4 +1,234 @@
|
|||||||
# Optics
|
# Optics
|
||||||
|
|
||||||
Refer to [Introduction to optics: lenses and prisms](https://medium.com/@gcanti/introduction-to-optics-lenses-and-prisms-3230e73bfcfe) for an introduction about functional optics.
|
Functional optics for composable data access and manipulation in Go.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Optics are first-class, composable references to parts of data structures. They provide a uniform interface for reading, writing, and transforming nested immutable data without verbose boilerplate code.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a lens for the Name field
|
||||||
|
nameLens := lens.MakeLens(
|
||||||
|
func(p Person) string { return p.Name },
|
||||||
|
func(p Person, name string) Person {
|
||||||
|
p.Name = name
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
person := Person{Name: "Alice", Age: 30}
|
||||||
|
|
||||||
|
// Get the name
|
||||||
|
name := nameLens.Get(person) // "Alice"
|
||||||
|
|
||||||
|
// Set a new name (returns a new Person)
|
||||||
|
updated := nameLens.Set("Bob")(person)
|
||||||
|
// person.Name is still "Alice", updated.Name is "Bob"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Optics Types
|
||||||
|
|
||||||
|
### Lens - Product Types (Structs)
|
||||||
|
Focus on a single field within a struct. Provides get and set operations.
|
||||||
|
|
||||||
|
**Use when:** Working with struct fields that always exist.
|
||||||
|
|
||||||
|
```go
|
||||||
|
ageLens := lens.MakeLens(
|
||||||
|
func(p Person) int { return p.Age },
|
||||||
|
func(p Person, age int) Person {
|
||||||
|
p.Age = age
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prism - Sum Types (Variants)
|
||||||
|
Focus on one variant of a sum type. Provides optional get and definite set.
|
||||||
|
|
||||||
|
**Use when:** Working with Either, Result, or custom sum types.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/IBM/fp-go/v2/optics/prism"
|
||||||
|
|
||||||
|
successPrism := prism.MakePrism(
|
||||||
|
func(r Result) option.Option[int] {
|
||||||
|
if s, ok := r.(Success); ok {
|
||||||
|
return option.Some(s.Value)
|
||||||
|
}
|
||||||
|
return option.None[int]()
|
||||||
|
},
|
||||||
|
func(v int) Result { return Success{Value: v} },
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Iso - Isomorphisms
|
||||||
|
Bidirectional transformation between equivalent types with no information loss.
|
||||||
|
|
||||||
|
**Use when:** Converting between equivalent representations (e.g., Celsius ↔ Fahrenheit).
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/IBM/fp-go/v2/optics/iso"
|
||||||
|
|
||||||
|
celsiusToFahrenheit := iso.MakeIso(
|
||||||
|
func(c float64) float64 { return c*9/5 + 32 },
|
||||||
|
func(f float64) float64 { return (f - 32) * 5 / 9 },
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional - Maybe Values
|
||||||
|
Focus on a value that may or may not exist.
|
||||||
|
|
||||||
|
**Use when:** Working with nullable fields or values that may be absent.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/IBM/fp-go/v2/optics/optional"
|
||||||
|
|
||||||
|
timeoutOptional := optional.MakeOptional(
|
||||||
|
func(c Config) option.Option[*int] {
|
||||||
|
return option.FromNillable(c.Timeout)
|
||||||
|
},
|
||||||
|
func(c Config, t *int) Config {
|
||||||
|
c.Timeout = t
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Traversal - Multiple Values
|
||||||
|
Focus on multiple values simultaneously, allowing batch operations.
|
||||||
|
|
||||||
|
**Use when:** Working with collections or updating multiple fields at once.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/optics/traversal"
|
||||||
|
TA "github.com/IBM/fp-go/v2/optics/traversal/array"
|
||||||
|
)
|
||||||
|
|
||||||
|
numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
// Double all elements
|
||||||
|
doubled := F.Pipe2(
|
||||||
|
numbers,
|
||||||
|
TA.Traversal[int](),
|
||||||
|
traversal.Modify[[]int, int](func(n int) int { return n * 2 }),
|
||||||
|
)
|
||||||
|
// Result: [2, 4, 6, 8, 10]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Composition
|
||||||
|
|
||||||
|
The real power of optics comes from composition:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Company struct {
|
||||||
|
Name string
|
||||||
|
Address Address
|
||||||
|
}
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
Street string
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individual lenses
|
||||||
|
addressLens := lens.MakeLens(
|
||||||
|
func(c Company) Address { return c.Address },
|
||||||
|
func(c Company, a Address) Company {
|
||||||
|
c.Address = a
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
cityLens := lens.MakeLens(
|
||||||
|
func(a Address) string { return a.City },
|
||||||
|
func(a Address, city string) Address {
|
||||||
|
a.City = city
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose to access city directly from company
|
||||||
|
companyCityLens := F.Pipe1(
|
||||||
|
addressLens,
|
||||||
|
lens.Compose[Company](cityLens),
|
||||||
|
)
|
||||||
|
|
||||||
|
company := Company{
|
||||||
|
Name: "Acme Corp",
|
||||||
|
Address: Address{Street: "Main St", City: "NYC"},
|
||||||
|
}
|
||||||
|
|
||||||
|
city := companyCityLens.Get(company) // "NYC"
|
||||||
|
updated := companyCityLens.Set("Boston")(company)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Optics Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
Iso[S, A]
|
||||||
|
↓
|
||||||
|
Lens[S, A]
|
||||||
|
↓
|
||||||
|
Optional[S, A]
|
||||||
|
↓
|
||||||
|
Traversal[S, A]
|
||||||
|
|
||||||
|
Prism[S, A]
|
||||||
|
↓
|
||||||
|
Optional[S, A]
|
||||||
|
↓
|
||||||
|
Traversal[S, A]
|
||||||
|
```
|
||||||
|
|
||||||
|
More specific optics can be converted to more general ones.
|
||||||
|
|
||||||
|
## Package Structure
|
||||||
|
|
||||||
|
- **optics/lens**: Lenses for product types (structs)
|
||||||
|
- **optics/prism**: Prisms for sum types (Either, Result, etc.)
|
||||||
|
- **optics/iso**: Isomorphisms for equivalent types
|
||||||
|
- **optics/optional**: Optional optics for maybe values
|
||||||
|
- **optics/traversal**: Traversals for multiple values
|
||||||
|
|
||||||
|
Each package includes specialized sub-packages for common patterns:
|
||||||
|
- **array**: Optics for arrays/slices
|
||||||
|
- **either**: Optics for Either types
|
||||||
|
- **option**: Optics for Option types
|
||||||
|
- **record**: Optics for maps
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For detailed documentation on each optic type, see:
|
||||||
|
- [Main Package Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2/optics)
|
||||||
|
- [Lens Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2/optics/lens)
|
||||||
|
- [Prism Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2/optics/prism)
|
||||||
|
- [Iso Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2/optics/iso)
|
||||||
|
- [Optional Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2/optics/optional)
|
||||||
|
- [Traversal Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2/optics/traversal)
|
||||||
|
|
||||||
|
## Further Reading
|
||||||
|
|
||||||
|
For an introduction to functional optics concepts:
|
||||||
|
- [Introduction to optics: lenses and prisms](https://medium.com/@gcanti/introduction-to-optics-lenses-and-prisms-3230e73bfcfe) by Giulio Canti
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See the [samples/lens](../samples/lens) directory for complete working examples.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Apache License 2.0 - See LICENSE file for details.
|
||||||
|
|||||||
305
v2/optics/iso/option/doc.go
Normal file
305
v2/optics/iso/option/doc.go
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
// 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 provides isomorphisms for working with Option types.
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
This package offers utilities to convert between regular values and Option-wrapped values,
|
||||||
|
particularly useful for handling zero values and optional data. It provides isomorphisms
|
||||||
|
that treat certain values (like zero values) as representing absence, mapping them to None,
|
||||||
|
while other values map to Some.
|
||||||
|
|
||||||
|
# Core Functionality
|
||||||
|
|
||||||
|
The main function in this package is FromZero, which creates an isomorphism between a
|
||||||
|
comparable type T and Option[T], treating the zero value as None.
|
||||||
|
|
||||||
|
# FromZero Isomorphism
|
||||||
|
|
||||||
|
FromZero creates a bidirectional transformation where:
|
||||||
|
- Forward (Get): T → Option[T]
|
||||||
|
- Zero value → None
|
||||||
|
- Non-zero value → Some(value)
|
||||||
|
- Reverse (ReverseGet): Option[T] → T
|
||||||
|
- None → Zero value
|
||||||
|
- Some(value) → value
|
||||||
|
|
||||||
|
# Basic Usage
|
||||||
|
|
||||||
|
Working with integers:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/optics/iso/option"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
isoInt := option.FromZero[int]()
|
||||||
|
|
||||||
|
// Convert zero to None
|
||||||
|
opt := isoInt.Get(0) // None[int]
|
||||||
|
|
||||||
|
// Convert non-zero to Some
|
||||||
|
opt = isoInt.Get(42) // Some(42)
|
||||||
|
|
||||||
|
// Convert None to zero
|
||||||
|
val := isoInt.ReverseGet(O.None[int]()) // 0
|
||||||
|
|
||||||
|
// Convert Some to value
|
||||||
|
val = isoInt.ReverseGet(O.Some(42)) // 42
|
||||||
|
|
||||||
|
# Use Cases
|
||||||
|
|
||||||
|
## Database Nullable Columns
|
||||||
|
|
||||||
|
Convert between database NULL and Go zero values:
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
Age *int // NULL in database
|
||||||
|
Email *string
|
||||||
|
}
|
||||||
|
|
||||||
|
ageIso := option.FromZero[*int]()
|
||||||
|
|
||||||
|
// Reading from database
|
||||||
|
var dbAge *int = nil
|
||||||
|
optAge := ageIso.Get(dbAge) // None[*int]
|
||||||
|
|
||||||
|
// Writing to database
|
||||||
|
userAge := 25
|
||||||
|
dbAge = ageIso.ReverseGet(O.Some(&userAge)) // &25
|
||||||
|
|
||||||
|
## Configuration with Defaults
|
||||||
|
|
||||||
|
Handle optional configuration values:
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Port int
|
||||||
|
Timeout int
|
||||||
|
MaxConn int
|
||||||
|
}
|
||||||
|
|
||||||
|
portIso := option.FromZero[int]()
|
||||||
|
|
||||||
|
// Use zero as "not configured"
|
||||||
|
config := Config{Port: 0, Timeout: 30, MaxConn: 100}
|
||||||
|
portOpt := portIso.Get(config.Port) // None[int] (use default)
|
||||||
|
|
||||||
|
// Set explicit value
|
||||||
|
config.Port = portIso.ReverseGet(O.Some(8080)) // 8080
|
||||||
|
|
||||||
|
## API Response Handling
|
||||||
|
|
||||||
|
Work with APIs that use zero values to indicate absence:
|
||||||
|
|
||||||
|
type APIResponse struct {
|
||||||
|
UserID int // 0 means not set
|
||||||
|
Score float64 // 0.0 means not available
|
||||||
|
Message string // "" means no message
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDIso := option.FromZero[int]()
|
||||||
|
scoreIso := option.FromZero[float64]()
|
||||||
|
messageIso := option.FromZero[string]()
|
||||||
|
|
||||||
|
response := APIResponse{UserID: 0, Score: 0.0, Message: ""}
|
||||||
|
|
||||||
|
userID := userIDIso.Get(response.UserID) // None[int]
|
||||||
|
score := scoreIso.Get(response.Score) // None[float64]
|
||||||
|
message := messageIso.Get(response.Message) // None[string]
|
||||||
|
|
||||||
|
## Validation Logic
|
||||||
|
|
||||||
|
Simplify required vs optional field validation:
|
||||||
|
|
||||||
|
type FormData struct {
|
||||||
|
Name string // Required
|
||||||
|
Email string // Required
|
||||||
|
Phone string // Optional (empty = not provided)
|
||||||
|
Comments string // Optional
|
||||||
|
}
|
||||||
|
|
||||||
|
phoneIso := option.FromZero[string]()
|
||||||
|
commentsIso := option.FromZero[string]()
|
||||||
|
|
||||||
|
form := FormData{
|
||||||
|
Name: "Alice",
|
||||||
|
Email: "alice@example.com",
|
||||||
|
Phone: "",
|
||||||
|
Comments: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check optional fields
|
||||||
|
phone := phoneIso.Get(form.Phone) // None[string]
|
||||||
|
comments := commentsIso.Get(form.Comments) // None[string]
|
||||||
|
|
||||||
|
// Validate: required fields must be non-empty
|
||||||
|
if form.Name == "" || form.Email == "" {
|
||||||
|
// Validation error
|
||||||
|
}
|
||||||
|
|
||||||
|
# Working with Different Types
|
||||||
|
|
||||||
|
## Strings
|
||||||
|
|
||||||
|
strIso := option.FromZero[string]()
|
||||||
|
|
||||||
|
opt := strIso.Get("") // None[string]
|
||||||
|
opt = strIso.Get("hello") // Some("hello")
|
||||||
|
|
||||||
|
val := strIso.ReverseGet(O.None[string]()) // ""
|
||||||
|
val = strIso.ReverseGet(O.Some("world")) // "world"
|
||||||
|
|
||||||
|
## Pointers
|
||||||
|
|
||||||
|
ptrIso := option.FromZero[*int]()
|
||||||
|
|
||||||
|
opt := ptrIso.Get(nil) // None[*int]
|
||||||
|
num := 42
|
||||||
|
opt = ptrIso.Get(&num) // Some(&num)
|
||||||
|
|
||||||
|
val := ptrIso.ReverseGet(O.None[*int]()) // nil
|
||||||
|
val = ptrIso.ReverseGet(O.Some(&num)) // &num
|
||||||
|
|
||||||
|
## Floating Point Numbers
|
||||||
|
|
||||||
|
floatIso := option.FromZero[float64]()
|
||||||
|
|
||||||
|
opt := floatIso.Get(0.0) // None[float64]
|
||||||
|
opt = floatIso.Get(3.14) // Some(3.14)
|
||||||
|
|
||||||
|
val := floatIso.ReverseGet(O.None[float64]()) // 0.0
|
||||||
|
val = floatIso.ReverseGet(O.Some(2.71)) // 2.71
|
||||||
|
|
||||||
|
## Booleans
|
||||||
|
|
||||||
|
boolIso := option.FromZero[bool]()
|
||||||
|
|
||||||
|
opt := boolIso.Get(false) // None[bool]
|
||||||
|
opt = boolIso.Get(true) // Some(true)
|
||||||
|
|
||||||
|
val := boolIso.ReverseGet(O.None[bool]()) // false
|
||||||
|
val = boolIso.ReverseGet(O.Some(true)) // true
|
||||||
|
|
||||||
|
# Composition with Other Optics
|
||||||
|
|
||||||
|
Combine with lenses for nested structures:
|
||||||
|
|
||||||
|
import (
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
I "github.com/IBM/fp-go/v2/optics/iso"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
Volume int // 0 means muted
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeLens := L.MakeLens(
|
||||||
|
func(s Settings) int { return s.Volume },
|
||||||
|
func(s Settings, v int) Settings {
|
||||||
|
s.Volume = v
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
volumeIso := option.FromZero[int]()
|
||||||
|
|
||||||
|
// Compose lens with iso
|
||||||
|
volumeOptLens := F.Pipe1(
|
||||||
|
volumeLens,
|
||||||
|
L.IMap[Settings](volumeIso.Get, volumeIso.ReverseGet),
|
||||||
|
)
|
||||||
|
|
||||||
|
settings := Settings{Volume: 0}
|
||||||
|
vol := volumeOptLens.Get(settings) // None[int] (muted)
|
||||||
|
|
||||||
|
// Set volume
|
||||||
|
updated := volumeOptLens.Set(O.Some(75))(settings)
|
||||||
|
// updated.Volume == 75
|
||||||
|
|
||||||
|
# Isomorphism Laws
|
||||||
|
|
||||||
|
FromZero satisfies the isomorphism round-trip laws:
|
||||||
|
|
||||||
|
1. **ReverseGet(Get(t)) == t** for all t: T
|
||||||
|
|
||||||
|
isoInt := option.FromZero[int]()
|
||||||
|
value := 42
|
||||||
|
result := isoInt.ReverseGet(isoInt.Get(value))
|
||||||
|
// result == 42
|
||||||
|
|
||||||
|
2. **Get(ReverseGet(opt)) == opt** for all opt: Option[T]
|
||||||
|
|
||||||
|
isoInt := option.FromZero[int]()
|
||||||
|
opt := O.Some(42)
|
||||||
|
result := isoInt.Get(isoInt.ReverseGet(opt))
|
||||||
|
// result == Some(42)
|
||||||
|
|
||||||
|
These laws ensure that the transformation is truly reversible with no information loss.
|
||||||
|
|
||||||
|
# Performance Considerations
|
||||||
|
|
||||||
|
The FromZero isomorphism is very efficient:
|
||||||
|
- No allocations for the iso structure itself
|
||||||
|
- Simple equality comparison for zero check
|
||||||
|
- Direct value unwrapping for ReverseGet
|
||||||
|
- No reflection or runtime type assertions
|
||||||
|
|
||||||
|
# Type Safety
|
||||||
|
|
||||||
|
The isomorphism is fully type-safe:
|
||||||
|
- Compile-time type checking ensures T is comparable
|
||||||
|
- Generic type parameters prevent type mismatches
|
||||||
|
- No runtime type assertions needed
|
||||||
|
- The compiler enforces correct usage
|
||||||
|
|
||||||
|
# Limitations
|
||||||
|
|
||||||
|
The FromZero isomorphism has some limitations to be aware of:
|
||||||
|
|
||||||
|
1. **Zero Value Ambiguity**: Cannot distinguish between "intentionally zero" and "absent"
|
||||||
|
- For int: 0 always maps to None, even if 0 is a valid value
|
||||||
|
- For string: "" always maps to None, even if empty string is valid
|
||||||
|
- Solution: Use a different representation (e.g., pointers) if zero is meaningful
|
||||||
|
|
||||||
|
2. **Comparable Constraint**: Only works with comparable types
|
||||||
|
- Cannot use with slices, maps, or functions
|
||||||
|
- Cannot use with structs containing non-comparable fields
|
||||||
|
- Solution: Use pointers to such types, or custom isomorphisms
|
||||||
|
|
||||||
|
3. **Boolean Limitation**: false always maps to None
|
||||||
|
- Cannot represent "explicitly false" vs "not set"
|
||||||
|
- Solution: Use *bool or a custom type if this distinction matters
|
||||||
|
|
||||||
|
# Related Packages
|
||||||
|
|
||||||
|
- github.com/IBM/fp-go/v2/optics/iso: Core isomorphism functionality
|
||||||
|
- github.com/IBM/fp-go/v2/option: Option type and operations
|
||||||
|
- github.com/IBM/fp-go/v2/optics/lens: Lenses for focused access
|
||||||
|
- github.com/IBM/fp-go/v2/optics/lens/option: Lenses for optional values
|
||||||
|
|
||||||
|
# See Also
|
||||||
|
|
||||||
|
For more information on isomorphisms and optics:
|
||||||
|
- optics/iso package documentation
|
||||||
|
- optics package overview
|
||||||
|
- option package documentation
|
||||||
|
*/
|
||||||
|
package option
|
||||||
|
|
||||||
|
// Made with Bob
|
||||||
366
v2/optics/lens/iso/doc.go
Normal file
366
v2/optics/lens/iso/doc.go
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
// 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 iso provides utilities for composing lenses with isomorphisms.
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
This package bridges lenses and isomorphisms, allowing you to transform the focus type
|
||||||
|
of a lens using an isomorphism. It provides functions to compose lenses with isomorphisms
|
||||||
|
and to create isomorphisms for common patterns like nullable pointers.
|
||||||
|
|
||||||
|
The key insight is that if you have a Lens[S, A] and an Iso[A, B], you can create a
|
||||||
|
Lens[S, B] by composing them. This allows you to work with transformed views of your
|
||||||
|
data without changing the underlying structure.
|
||||||
|
|
||||||
|
# Core Functions
|
||||||
|
|
||||||
|
## FromNillable
|
||||||
|
|
||||||
|
Creates an isomorphism between a nullable pointer and an Option type:
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Timeout *int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create isomorphism: *int ↔ Option[int]
|
||||||
|
timeoutIso := iso.FromNillable[int]()
|
||||||
|
|
||||||
|
// nil → None, &value → Some(value)
|
||||||
|
opt := timeoutIso.Get(nil) // None[int]
|
||||||
|
num := 42
|
||||||
|
opt = timeoutIso.Get(&num) // Some(42)
|
||||||
|
|
||||||
|
// None → nil, Some(value) → &value
|
||||||
|
ptr := timeoutIso.ReverseGet(O.None[int]()) // nil
|
||||||
|
ptr = timeoutIso.ReverseGet(O.Some(42)) // &42
|
||||||
|
|
||||||
|
## Compose
|
||||||
|
|
||||||
|
Composes a lens with an isomorphism to transform the focus type:
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Celsius float64
|
||||||
|
type Fahrenheit float64
|
||||||
|
|
||||||
|
type Weather struct {
|
||||||
|
Temperature Celsius
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens to access temperature
|
||||||
|
tempLens := L.MakeLens(
|
||||||
|
func(w Weather) Celsius { return w.Temperature },
|
||||||
|
func(w Weather, t Celsius) Weather {
|
||||||
|
w.Temperature = t
|
||||||
|
return w
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isomorphism: Celsius ↔ Fahrenheit
|
||||||
|
celsiusToFahrenheit := I.MakeIso(
|
||||||
|
func(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) },
|
||||||
|
func(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose to work with Fahrenheit
|
||||||
|
tempFahrenheitLens := F.Pipe1(
|
||||||
|
tempLens,
|
||||||
|
iso.Compose[Weather, Celsius, Fahrenheit](celsiusToFahrenheit),
|
||||||
|
)
|
||||||
|
|
||||||
|
weather := Weather{Temperature: 20} // 20°C
|
||||||
|
tempF := tempFahrenheitLens.Get(weather) // 68°F
|
||||||
|
updated := tempFahrenheitLens.Set(86)(weather) // Set to 86°F (30°C)
|
||||||
|
|
||||||
|
# Use Cases
|
||||||
|
|
||||||
|
## Working with Nullable Fields
|
||||||
|
|
||||||
|
Convert between nullable pointers and Option types:
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
Username string
|
||||||
|
Password *string // Nullable
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppConfig struct {
|
||||||
|
Database *DatabaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens to database config
|
||||||
|
dbLens := L.MakeLens(
|
||||||
|
func(c AppConfig) *DatabaseConfig { return c.Database },
|
||||||
|
func(c AppConfig, db *DatabaseConfig) AppConfig {
|
||||||
|
c.Database = db
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isomorphism for nullable pointer
|
||||||
|
dbIso := iso.FromNillable[DatabaseConfig]()
|
||||||
|
|
||||||
|
// Compose to work with Option
|
||||||
|
dbOptLens := F.Pipe1(
|
||||||
|
dbLens,
|
||||||
|
iso.Compose[AppConfig, *DatabaseConfig, O.Option[DatabaseConfig]](dbIso),
|
||||||
|
)
|
||||||
|
|
||||||
|
config := AppConfig{Database: nil}
|
||||||
|
dbOpt := dbOptLens.Get(config) // None[DatabaseConfig]
|
||||||
|
|
||||||
|
// Set with Some
|
||||||
|
newDB := DatabaseConfig{Host: "localhost", Port: 5432}
|
||||||
|
updated := dbOptLens.Set(O.Some(newDB))(config)
|
||||||
|
|
||||||
|
## Unit Conversions
|
||||||
|
|
||||||
|
Work with different units of measurement:
|
||||||
|
|
||||||
|
type Distance struct {
|
||||||
|
Meters float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Kilometers float64
|
||||||
|
type Miles float64
|
||||||
|
|
||||||
|
// Lens to meters
|
||||||
|
metersLens := L.MakeLens(
|
||||||
|
func(d Distance) float64 { return d.Meters },
|
||||||
|
func(d Distance, m float64) Distance {
|
||||||
|
d.Meters = m
|
||||||
|
return d
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isomorphism: meters ↔ kilometers
|
||||||
|
metersToKm := I.MakeIso(
|
||||||
|
func(m float64) Kilometers { return Kilometers(m / 1000) },
|
||||||
|
func(km Kilometers) float64 { return float64(km * 1000) },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose to work with kilometers
|
||||||
|
kmLens := F.Pipe1(
|
||||||
|
metersLens,
|
||||||
|
iso.Compose[Distance, float64, Kilometers](metersToKm),
|
||||||
|
)
|
||||||
|
|
||||||
|
distance := Distance{Meters: 5000}
|
||||||
|
km := kmLens.Get(distance) // 5 km
|
||||||
|
updated := kmLens.Set(Kilometers(10))(distance) // 10000 meters
|
||||||
|
|
||||||
|
## Type Wrappers
|
||||||
|
|
||||||
|
Work with newtype wrappers:
|
||||||
|
|
||||||
|
type UserId int
|
||||||
|
type User struct {
|
||||||
|
ID UserId
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens to user ID
|
||||||
|
idLens := L.MakeLens(
|
||||||
|
func(u User) UserId { return u.ID },
|
||||||
|
func(u User, id UserId) User {
|
||||||
|
u.ID = id
|
||||||
|
return u
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isomorphism: UserId ↔ int
|
||||||
|
userIdIso := I.MakeIso(
|
||||||
|
func(id UserId) int { return int(id) },
|
||||||
|
func(i int) UserId { return UserId(i) },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose to work with raw int
|
||||||
|
idIntLens := F.Pipe1(
|
||||||
|
idLens,
|
||||||
|
iso.Compose[User, UserId, int](userIdIso),
|
||||||
|
)
|
||||||
|
|
||||||
|
user := User{ID: 42, Name: "Alice"}
|
||||||
|
rawId := idIntLens.Get(user) // 42 (int)
|
||||||
|
updated := idIntLens.Set(100)(user) // UserId(100)
|
||||||
|
|
||||||
|
## Nested Nullable Fields
|
||||||
|
|
||||||
|
Safely navigate through nullable nested structures:
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
Street string
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Address *Address
|
||||||
|
}
|
||||||
|
|
||||||
|
type Company struct {
|
||||||
|
Name string
|
||||||
|
CEO *Person
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens to CEO
|
||||||
|
ceoLens := L.MakeLens(
|
||||||
|
func(c Company) *Person { return c.CEO },
|
||||||
|
func(c Company, p *Person) Company {
|
||||||
|
c.CEO = p
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isomorphism for nullable person
|
||||||
|
personIso := iso.FromNillable[Person]()
|
||||||
|
|
||||||
|
// Compose to work with Option[Person]
|
||||||
|
ceoOptLens := F.Pipe1(
|
||||||
|
ceoLens,
|
||||||
|
iso.Compose[Company, *Person, O.Option[Person]](personIso),
|
||||||
|
)
|
||||||
|
|
||||||
|
company := Company{Name: "Acme Corp", CEO: nil}
|
||||||
|
ceo := ceoOptLens.Get(company) // None[Person]
|
||||||
|
|
||||||
|
// Set CEO
|
||||||
|
newCEO := Person{Name: "Alice", Address: nil}
|
||||||
|
updated := ceoOptLens.Set(O.Some(newCEO))(company)
|
||||||
|
|
||||||
|
# Composition Patterns
|
||||||
|
|
||||||
|
## Chaining Multiple Isomorphisms
|
||||||
|
|
||||||
|
type Meters float64
|
||||||
|
type Kilometers float64
|
||||||
|
type Miles float64
|
||||||
|
|
||||||
|
type Journey struct {
|
||||||
|
Distance Meters
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens to distance
|
||||||
|
distLens := L.MakeLens(
|
||||||
|
func(j Journey) Meters { return j.Distance },
|
||||||
|
func(j Journey, d Meters) Journey {
|
||||||
|
j.Distance = d
|
||||||
|
return j
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isomorphisms
|
||||||
|
metersToKm := I.MakeIso(
|
||||||
|
func(m Meters) Kilometers { return Kilometers(m / 1000) },
|
||||||
|
func(km Kilometers) Meters { return Meters(km * 1000) },
|
||||||
|
)
|
||||||
|
|
||||||
|
kmToMiles := I.MakeIso(
|
||||||
|
func(km Kilometers) Miles { return Miles(km * 0.621371) },
|
||||||
|
func(mi Miles) Kilometers { return Kilometers(mi / 0.621371) },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose lens with chained isomorphisms
|
||||||
|
milesLens := F.Pipe2(
|
||||||
|
distLens,
|
||||||
|
iso.Compose[Journey, Meters, Kilometers](metersToKm),
|
||||||
|
iso.Compose[Journey, Kilometers, Miles](kmToMiles),
|
||||||
|
)
|
||||||
|
|
||||||
|
journey := Journey{Distance: 5000} // 5000 meters
|
||||||
|
miles := milesLens.Get(journey) // ~3.11 miles
|
||||||
|
|
||||||
|
## Combining with Optional Lenses
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Database *DatabaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens to database (nullable)
|
||||||
|
dbLens := L.MakeLens(
|
||||||
|
func(c Config) *DatabaseConfig { return c.Database },
|
||||||
|
func(c Config, db *DatabaseConfig) Config {
|
||||||
|
c.Database = db
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Convert to Option lens
|
||||||
|
dbIso := iso.FromNillable[DatabaseConfig]()
|
||||||
|
dbOptLens := F.Pipe1(
|
||||||
|
dbLens,
|
||||||
|
iso.Compose[Config, *DatabaseConfig, O.Option[DatabaseConfig]](dbIso),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now compose with lens to port
|
||||||
|
portLens := L.MakeLens(
|
||||||
|
func(db DatabaseConfig) int { return db.Port },
|
||||||
|
func(db DatabaseConfig, port int) DatabaseConfig {
|
||||||
|
db.Port = port
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Use ComposeOption to handle the Option
|
||||||
|
defaultDB := DatabaseConfig{Port: 5432}
|
||||||
|
configPortLens := F.Pipe1(
|
||||||
|
dbOptLens,
|
||||||
|
L.ComposeOption[Config, int](defaultDB)(portLens),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Performance Considerations
|
||||||
|
|
||||||
|
Composing lenses with isomorphisms is efficient:
|
||||||
|
- No additional allocations beyond the lens and iso structures
|
||||||
|
- Composition creates function closures but is still performant
|
||||||
|
- The isomorphism transformations are applied on-demand
|
||||||
|
- Consider caching composed lenses for frequently used paths
|
||||||
|
|
||||||
|
# Type Safety
|
||||||
|
|
||||||
|
All operations are fully type-safe:
|
||||||
|
- Compile-time type checking ensures correct composition
|
||||||
|
- Generic type parameters prevent type mismatches
|
||||||
|
- No runtime type assertions needed
|
||||||
|
- The compiler enforces that isomorphisms are properly reversible
|
||||||
|
|
||||||
|
# Related Packages
|
||||||
|
|
||||||
|
- github.com/IBM/fp-go/v2/optics/lens: Core lens functionality
|
||||||
|
- github.com/IBM/fp-go/v2/optics/iso: Core isomorphism functionality
|
||||||
|
- github.com/IBM/fp-go/v2/optics/iso/lens: Convert isomorphisms to lenses
|
||||||
|
- github.com/IBM/fp-go/v2/option: Option type and operations
|
||||||
|
- github.com/IBM/fp-go/v2/function: Function composition utilities
|
||||||
|
|
||||||
|
# See Also
|
||||||
|
|
||||||
|
For more information on lenses and isomorphisms:
|
||||||
|
- optics/lens package documentation
|
||||||
|
- optics/iso package documentation
|
||||||
|
- optics package overview
|
||||||
|
*/
|
||||||
|
package iso
|
||||||
|
|
||||||
|
// Made with Bob
|
||||||
479
v2/optics/optional/doc.go
Normal file
479
v2/optics/optional/doc.go
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
// 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 optional provides optional optics for focusing on values that may not exist.
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
An Optional is an optic that focuses on a subpart of a data structure that may or may not
|
||||||
|
be present. Unlike lenses which always focus on an existing field, optionals handle cases
|
||||||
|
where the target value might be absent, returning Option[A] instead of A.
|
||||||
|
|
||||||
|
Optionals are the bridge between lenses (which always succeed) and prisms (which may fail
|
||||||
|
to match). They combine aspects of both:
|
||||||
|
- Like lenses: Focus on a specific location in a structure
|
||||||
|
- Like prisms: The value at that location may not exist
|
||||||
|
|
||||||
|
Optionals are essential for:
|
||||||
|
- Working with nullable fields (pointers that may be nil)
|
||||||
|
- Accessing nested optional values
|
||||||
|
- Conditional updates based on value presence
|
||||||
|
- Safe navigation through potentially missing data
|
||||||
|
|
||||||
|
# Mathematical Foundation
|
||||||
|
|
||||||
|
An Optional[S, A] consists of two operations:
|
||||||
|
- GetOption: S → Option[A] (try to extract A from S, may return None)
|
||||||
|
- Set: A → S → S (update A in S, may be a no-op if value doesn't exist)
|
||||||
|
|
||||||
|
Optionals must satisfy the optional laws:
|
||||||
|
1. GetOptionSet: if GetOption(s) == Some(a), then GetOption(Set(a)(s)) == Some(a)
|
||||||
|
2. SetGetOption: if GetOption(s) == Some(a), then Set(a)(s) preserves other parts of s
|
||||||
|
3. SetSet: Set(a2)(Set(a1)(s)) == Set(a2)(s)
|
||||||
|
|
||||||
|
# Basic Usage
|
||||||
|
|
||||||
|
Creating an optional for a nullable field:
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Timeout *int
|
||||||
|
MaxSize *int
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutOptional := optional.MakeOptional(
|
||||||
|
func(c Config) option.Option[*int] {
|
||||||
|
return option.FromNillable(c.Timeout)
|
||||||
|
},
|
||||||
|
func(c Config, t *int) Config {
|
||||||
|
c.Timeout = t
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
config := Config{Timeout: nil, MaxSize: ptr(100)}
|
||||||
|
|
||||||
|
// Get returns None for nil
|
||||||
|
timeout := timeoutOptional.GetOption(config) // None[*int]
|
||||||
|
|
||||||
|
// Set updates the value
|
||||||
|
newTimeout := 30
|
||||||
|
updated := timeoutOptional.Set(&newTimeout)(config)
|
||||||
|
// updated.Timeout points to 30
|
||||||
|
|
||||||
|
# Working with Pointers
|
||||||
|
|
||||||
|
For pointer-based structures, use MakeOptionalRef which handles copying automatically:
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Database *Database
|
||||||
|
}
|
||||||
|
|
||||||
|
dbOptional := optional.MakeOptionalRef(
|
||||||
|
func(c *Config) option.Option[*Database] {
|
||||||
|
return option.FromNillable(c.Database)
|
||||||
|
},
|
||||||
|
func(c *Config, db *Database) *Config {
|
||||||
|
c.Database = db
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
config := &Config{Database: nil}
|
||||||
|
|
||||||
|
// Get returns None when database is nil
|
||||||
|
db := dbOptional.GetOption(config) // None[*Database]
|
||||||
|
|
||||||
|
// Set creates a new config with the database
|
||||||
|
newDB := &Database{Host: "localhost", Port: 5432}
|
||||||
|
updated := dbOptional.Set(newDB)(config)
|
||||||
|
// config.Database is still nil, updated.Database points to newDB
|
||||||
|
|
||||||
|
# Identity Optional
|
||||||
|
|
||||||
|
The identity optional focuses on the entire structure:
|
||||||
|
|
||||||
|
idOpt := optional.Id[Config]()
|
||||||
|
|
||||||
|
config := Config{Timeout: ptr(30)}
|
||||||
|
value := idOpt.GetOption(config) // Some(config)
|
||||||
|
updated := idOpt.Set(Config{Timeout: ptr(60)})(config)
|
||||||
|
|
||||||
|
# Composing Optionals
|
||||||
|
|
||||||
|
Optionals can be composed to navigate through nested optional structures:
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
Street string
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Address *Address
|
||||||
|
}
|
||||||
|
|
||||||
|
addressOpt := optional.MakeOptional(
|
||||||
|
func(p Person) option.Option[*Address] {
|
||||||
|
return option.FromNillable(p.Address)
|
||||||
|
},
|
||||||
|
func(p Person, a *Address) Person {
|
||||||
|
p.Address = a
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
cityOpt := optional.MakeOptionalRef(
|
||||||
|
func(a *Address) option.Option[string] {
|
||||||
|
if a == nil {
|
||||||
|
return option.None[string]()
|
||||||
|
}
|
||||||
|
return option.Some(a.City)
|
||||||
|
},
|
||||||
|
func(a *Address, city string) *Address {
|
||||||
|
a.City = city
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose to access city from person
|
||||||
|
personCityOpt := F.Pipe1(
|
||||||
|
addressOpt,
|
||||||
|
optional.Compose[Person, *Address, string](cityOpt),
|
||||||
|
)
|
||||||
|
|
||||||
|
person := Person{Name: "Alice", Address: nil}
|
||||||
|
|
||||||
|
// Get returns None when address is nil
|
||||||
|
city := personCityOpt.GetOption(person) // None[string]
|
||||||
|
|
||||||
|
// Set updates the city if address exists
|
||||||
|
withAddress := Person{
|
||||||
|
Name: "Alice",
|
||||||
|
Address: &Address{Street: "Main St", City: "NYC"},
|
||||||
|
}
|
||||||
|
updated := personCityOpt.Set("Boston")(withAddress)
|
||||||
|
// updated.Address.City == "Boston"
|
||||||
|
|
||||||
|
# From Predicate
|
||||||
|
|
||||||
|
Create an optional that only focuses on values satisfying a predicate:
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
ageOpt := optional.FromPredicate[User, int](
|
||||||
|
func(age int) bool { return age >= 18 },
|
||||||
|
)(
|
||||||
|
func(u User) int { return u.Age },
|
||||||
|
func(u User, age int) User {
|
||||||
|
u.Age = age
|
||||||
|
return u
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
adult := User{Age: 25}
|
||||||
|
age := ageOpt.GetOption(adult) // Some(25)
|
||||||
|
|
||||||
|
minor := User{Age: 15}
|
||||||
|
minorAge := ageOpt.GetOption(minor) // None[int]
|
||||||
|
|
||||||
|
// Set only works if predicate is satisfied
|
||||||
|
updated := ageOpt.Set(30)(adult) // Age becomes 30
|
||||||
|
unchanged := ageOpt.Set(30)(minor) // Age stays 15 (predicate fails)
|
||||||
|
|
||||||
|
# Modifying Values
|
||||||
|
|
||||||
|
Use ModifyOption to transform values that exist:
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
Value *int
|
||||||
|
}
|
||||||
|
|
||||||
|
valueOpt := optional.MakeOptional(
|
||||||
|
func(c Counter) option.Option[*int] {
|
||||||
|
return option.FromNillable(c.Value)
|
||||||
|
},
|
||||||
|
func(c Counter, v *int) Counter {
|
||||||
|
c.Value = v
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
counter := Counter{Value: ptr(5)}
|
||||||
|
|
||||||
|
// Increment if value exists
|
||||||
|
incremented := F.Pipe3(
|
||||||
|
counter,
|
||||||
|
valueOpt,
|
||||||
|
optional.ModifyOption[Counter, *int](func(v *int) *int {
|
||||||
|
newVal := *v + 1
|
||||||
|
return &newVal
|
||||||
|
}),
|
||||||
|
option.GetOrElse(F.Constant(counter)),
|
||||||
|
)
|
||||||
|
// incremented.Value points to 6
|
||||||
|
|
||||||
|
// No change if value is nil
|
||||||
|
nilCounter := Counter{Value: nil}
|
||||||
|
result := F.Pipe3(
|
||||||
|
nilCounter,
|
||||||
|
valueOpt,
|
||||||
|
optional.ModifyOption[Counter, *int](func(v *int) *int {
|
||||||
|
newVal := *v + 1
|
||||||
|
return &newVal
|
||||||
|
}),
|
||||||
|
option.GetOrElse(F.Constant(nilCounter)),
|
||||||
|
)
|
||||||
|
// result.Value is still nil
|
||||||
|
|
||||||
|
# Bidirectional Mapping
|
||||||
|
|
||||||
|
Transform the focus type of an optional:
|
||||||
|
|
||||||
|
type Celsius float64
|
||||||
|
type Fahrenheit float64
|
||||||
|
|
||||||
|
type Weather struct {
|
||||||
|
Temperature *Celsius
|
||||||
|
}
|
||||||
|
|
||||||
|
tempCelsiusOpt := optional.MakeOptional(
|
||||||
|
func(w Weather) option.Option[*Celsius] {
|
||||||
|
return option.FromNillable(w.Temperature)
|
||||||
|
},
|
||||||
|
func(w Weather, t *Celsius) Weather {
|
||||||
|
w.Temperature = t
|
||||||
|
return w
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create optional that works with Fahrenheit
|
||||||
|
tempFahrenheitOpt := F.Pipe1(
|
||||||
|
tempCelsiusOpt,
|
||||||
|
optional.IMap[Weather, *Celsius, *Fahrenheit](
|
||||||
|
func(c *Celsius) *Fahrenheit {
|
||||||
|
f := Fahrenheit(*c*9/5 + 32)
|
||||||
|
return &f
|
||||||
|
},
|
||||||
|
func(f *Fahrenheit) *Celsius {
|
||||||
|
c := Celsius((*f - 32) * 5 / 9)
|
||||||
|
return &c
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
celsius := Celsius(20)
|
||||||
|
weather := Weather{Temperature: &celsius}
|
||||||
|
|
||||||
|
tempF := tempFahrenheitOpt.GetOption(weather) // Some(68°F)
|
||||||
|
|
||||||
|
# Real-World Example: Configuration with Defaults
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppConfig struct {
|
||||||
|
Database *DatabaseConfig
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
dbOpt := optional.MakeOptional(
|
||||||
|
func(c AppConfig) option.Option[*DatabaseConfig] {
|
||||||
|
return option.FromNillable(c.Database)
|
||||||
|
},
|
||||||
|
func(c AppConfig, db *DatabaseConfig) AppConfig {
|
||||||
|
c.Database = db
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
dbHostOpt := optional.MakeOptionalRef(
|
||||||
|
func(db *DatabaseConfig) option.Option[string] {
|
||||||
|
if db == nil {
|
||||||
|
return option.None[string]()
|
||||||
|
}
|
||||||
|
return option.Some(db.Host)
|
||||||
|
},
|
||||||
|
func(db *DatabaseConfig, host string) *DatabaseConfig {
|
||||||
|
db.Host = host
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose to access database host
|
||||||
|
appDbHostOpt := F.Pipe1(
|
||||||
|
dbOpt,
|
||||||
|
optional.Compose[AppConfig, *DatabaseConfig, string](dbHostOpt),
|
||||||
|
)
|
||||||
|
|
||||||
|
config := AppConfig{Database: nil, Debug: true}
|
||||||
|
|
||||||
|
// Get returns None when database is not configured
|
||||||
|
host := appDbHostOpt.GetOption(config) // None[string]
|
||||||
|
|
||||||
|
// Set creates database if needed
|
||||||
|
withDB := AppConfig{
|
||||||
|
Database: &DatabaseConfig{Host: "localhost", Port: 5432},
|
||||||
|
Debug: true,
|
||||||
|
}
|
||||||
|
updated := appDbHostOpt.Set("prod.example.com")(withDB)
|
||||||
|
// updated.Database.Host == "prod.example.com"
|
||||||
|
|
||||||
|
# Real-World Example: Safe Navigation
|
||||||
|
|
||||||
|
type Company struct {
|
||||||
|
Name string
|
||||||
|
CEO *Person
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Address *Address
|
||||||
|
}
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
|
ceoOpt := optional.MakeOptional(
|
||||||
|
func(c Company) option.Option[*Person] {
|
||||||
|
return option.FromNillable(c.CEO)
|
||||||
|
},
|
||||||
|
func(c Company, p *Person) Company {
|
||||||
|
c.CEO = p
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
addressOpt := optional.MakeOptionalRef(
|
||||||
|
func(p *Person) option.Option[*Address] {
|
||||||
|
return option.FromNillable(p.Address)
|
||||||
|
},
|
||||||
|
func(p *Person, a *Address) *Person {
|
||||||
|
p.Address = a
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
cityOpt := optional.MakeOptionalRef(
|
||||||
|
func(a *Address) option.Option[string] {
|
||||||
|
if a == nil {
|
||||||
|
return option.None[string]()
|
||||||
|
}
|
||||||
|
return option.Some(a.City)
|
||||||
|
},
|
||||||
|
func(a *Address, city string) *Address {
|
||||||
|
a.City = city
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose all optionals for safe navigation
|
||||||
|
ceoCityOpt := F.Pipe2(
|
||||||
|
ceoOpt,
|
||||||
|
optional.Compose[Company, *Person, *Address](addressOpt),
|
||||||
|
optional.Compose[Company, *Address, string](cityOpt),
|
||||||
|
)
|
||||||
|
|
||||||
|
company := Company{Name: "Acme Corp", CEO: nil}
|
||||||
|
|
||||||
|
// Safe navigation returns None at any missing level
|
||||||
|
city := ceoCityOpt.GetOption(company) // None[string]
|
||||||
|
|
||||||
|
# Optionals in the Optics Hierarchy
|
||||||
|
|
||||||
|
Optionals sit between lenses and traversals in the optics hierarchy:
|
||||||
|
|
||||||
|
Lens[S, A]
|
||||||
|
↓
|
||||||
|
Optional[S, A]
|
||||||
|
↓
|
||||||
|
Traversal[S, A]
|
||||||
|
|
||||||
|
Prism[S, A]
|
||||||
|
↓
|
||||||
|
Optional[S, A]
|
||||||
|
|
||||||
|
This means:
|
||||||
|
- Every Lens can be converted to an Optional (value always exists)
|
||||||
|
- Every Prism can be converted to an Optional (variant may not match)
|
||||||
|
- Every Optional can be converted to a Traversal (0 or 1 values)
|
||||||
|
|
||||||
|
# Performance Considerations
|
||||||
|
|
||||||
|
Optionals are efficient:
|
||||||
|
- No reflection - all operations are type-safe at compile time
|
||||||
|
- Minimal allocations - optionals themselves are lightweight
|
||||||
|
- GetOption short-circuits on None
|
||||||
|
- Set operations create new copies (immutability)
|
||||||
|
|
||||||
|
For best performance:
|
||||||
|
- Use MakeOptionalRef for pointer structures to ensure proper copying
|
||||||
|
- Cache composed optionals rather than recomposing
|
||||||
|
- Consider batch operations when updating multiple optional values
|
||||||
|
|
||||||
|
# Type Safety
|
||||||
|
|
||||||
|
Optionals are fully type-safe:
|
||||||
|
- Compile-time type checking
|
||||||
|
- No runtime type assertions
|
||||||
|
- Generic type parameters ensure correctness
|
||||||
|
- Composition maintains type relationships
|
||||||
|
|
||||||
|
# Function Reference
|
||||||
|
|
||||||
|
Core Optional Creation:
|
||||||
|
- MakeOptional: Create an optional from getter and setter functions
|
||||||
|
- MakeOptionalRef: Create an optional for pointer-based structures
|
||||||
|
- Id: Create an identity optional
|
||||||
|
- IdRef: Create an identity optional for pointers
|
||||||
|
|
||||||
|
Composition:
|
||||||
|
- Compose: Compose two optionals
|
||||||
|
- ComposeRef: Compose optionals for pointer structures
|
||||||
|
|
||||||
|
Transformation:
|
||||||
|
- ModifyOption: Transform a value through an optional (returns Option[S])
|
||||||
|
- SetOption: Set a value through an optional (returns Option[S])
|
||||||
|
- IMap: Bidirectionally map an optional
|
||||||
|
- IChain: Bidirectionally map with optional results
|
||||||
|
- IChainAny: Map to/from any type
|
||||||
|
|
||||||
|
Predicate-Based:
|
||||||
|
- FromPredicate: Create optional from predicate
|
||||||
|
- FromPredicateRef: Create optional from predicate (ref version)
|
||||||
|
|
||||||
|
# Related Packages
|
||||||
|
|
||||||
|
- github.com/IBM/fp-go/v2/optics/lens: Lenses for fields that always exist
|
||||||
|
- github.com/IBM/fp-go/v2/optics/prism: Prisms for sum types
|
||||||
|
- github.com/IBM/fp-go/v2/optics/traversal: Traversals for multiple values
|
||||||
|
- github.com/IBM/fp-go/v2/option: Optional values
|
||||||
|
- github.com/IBM/fp-go/v2/endomorphism: Endomorphisms (A → A functions)
|
||||||
|
*/
|
||||||
|
package optional
|
||||||
|
|
||||||
|
// Made with Bob
|
||||||
495
v2/optics/traversal/doc.go
Normal file
495
v2/optics/traversal/doc.go
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
// 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 traversal provides traversals - optics for focusing on multiple values simultaneously.
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
A Traversal is an optic that focuses on zero or more values within a data structure,
|
||||||
|
allowing you to view, modify, or fold over multiple elements at once. Unlike lenses
|
||||||
|
which focus on a single field, or prisms which focus on one variant, traversals can
|
||||||
|
target collections, multiple fields, or any number of values.
|
||||||
|
|
||||||
|
Traversals are the most general optic and sit at the bottom of the optics hierarchy.
|
||||||
|
They are essential for:
|
||||||
|
- Working with collections (arrays, slices, maps)
|
||||||
|
- Batch operations on multiple fields
|
||||||
|
- Filtering and transforming multiple values
|
||||||
|
- Aggregating data from multiple sources
|
||||||
|
- Applying the same operation to all matching elements
|
||||||
|
|
||||||
|
# Mathematical Foundation
|
||||||
|
|
||||||
|
A Traversal[S, A] is defined using higher-kinded types and applicative functors.
|
||||||
|
In practical terms, it provides operations to:
|
||||||
|
- Modify: Apply a function to all focused values
|
||||||
|
- Set: Replace all focused values with a constant
|
||||||
|
- FoldMap: Map each value to a monoid and combine results
|
||||||
|
- GetAll: Collect all focused values into a list
|
||||||
|
|
||||||
|
Traversals must satisfy the traversal laws:
|
||||||
|
1. Identity: traverse(Identity, id) == Identity
|
||||||
|
2. Composition: traverse(Compose(F, G), f) == Compose(traverse(F, traverse(G, f)))
|
||||||
|
|
||||||
|
These laws ensure that traversals compose properly and behave consistently.
|
||||||
|
|
||||||
|
# Basic Usage
|
||||||
|
|
||||||
|
Creating a traversal for array elements:
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
T "github.com/IBM/fp-go/v2/optics/traversal"
|
||||||
|
TA "github.com/IBM/fp-go/v2/optics/traversal/array"
|
||||||
|
)
|
||||||
|
|
||||||
|
numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
// Get all elements
|
||||||
|
all := T.GetAll(numbers)(TA.Traversal[int]())
|
||||||
|
// Result: [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
// Modify all elements
|
||||||
|
doubled := F.Pipe2(
|
||||||
|
numbers,
|
||||||
|
TA.Traversal[int](),
|
||||||
|
T.Modify[[]int, int](func(n int) int { return n * 2 }),
|
||||||
|
)
|
||||||
|
// Result: [2, 4, 6, 8, 10]
|
||||||
|
|
||||||
|
// Set all elements to a constant
|
||||||
|
allTens := F.Pipe2(
|
||||||
|
numbers,
|
||||||
|
TA.Traversal[int](),
|
||||||
|
T.Set[[]int, int](10),
|
||||||
|
)
|
||||||
|
// Result: [10, 10, 10, 10, 10]
|
||||||
|
|
||||||
|
# Identity Traversal
|
||||||
|
|
||||||
|
The identity traversal focuses on the entire structure:
|
||||||
|
|
||||||
|
idTrav := T.Id[int, int]()
|
||||||
|
|
||||||
|
value := 42
|
||||||
|
result := T.Modify[int, int](func(n int) int { return n * 2 })(idTrav)(value)
|
||||||
|
// Result: 84
|
||||||
|
|
||||||
|
# Folding with Traversals
|
||||||
|
|
||||||
|
Aggregate values using monoids:
|
||||||
|
|
||||||
|
import (
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
N "github.com/IBM/fp-go/v2/number"
|
||||||
|
)
|
||||||
|
|
||||||
|
numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
// Sum all elements
|
||||||
|
sum := F.Pipe2(
|
||||||
|
numbers,
|
||||||
|
TA.Traversal[int](),
|
||||||
|
T.FoldMap[int, []int, int](F.Identity[int]),
|
||||||
|
)(N.MonoidSum[int]())
|
||||||
|
// Result: 15
|
||||||
|
|
||||||
|
// Product of all elements
|
||||||
|
product := F.Pipe2(
|
||||||
|
numbers,
|
||||||
|
TA.Traversal[int](),
|
||||||
|
T.FoldMap[int, []int, int](F.Identity[int]),
|
||||||
|
)(N.MonoidProduct[int]())
|
||||||
|
// Result: 120
|
||||||
|
|
||||||
|
# Composing Traversals
|
||||||
|
|
||||||
|
Traversals can be composed to focus on nested collections:
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Friends []string
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{Name: "Alice", Friends: []string{"Bob", "Charlie"}},
|
||||||
|
{Name: "Bob", Friends: []string{"Alice", "David"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traversal for people array
|
||||||
|
peopleTrav := TA.Traversal[Person]()
|
||||||
|
|
||||||
|
// Traversal for friends array within a person
|
||||||
|
friendsTrav := T.MakeTraversal(func(p Person) []string {
|
||||||
|
return p.Friends
|
||||||
|
})
|
||||||
|
|
||||||
|
// Compose to access all friends of all people
|
||||||
|
allFriendsTrav := F.Pipe1(
|
||||||
|
peopleTrav,
|
||||||
|
T.Compose[[]Person, Person, string, ...](friendsTrav),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get all friends
|
||||||
|
allFriends := T.GetAll(people)(allFriendsTrav)
|
||||||
|
// Result: ["Bob", "Charlie", "Alice", "David"]
|
||||||
|
|
||||||
|
# Working with Records (Maps)
|
||||||
|
|
||||||
|
Traverse over map values:
|
||||||
|
|
||||||
|
import TR "github.com/IBM/fp-go/v2/optics/traversal/record"
|
||||||
|
|
||||||
|
scores := map[string]int{
|
||||||
|
"Alice": 85,
|
||||||
|
"Bob": 92,
|
||||||
|
"Charlie": 78,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all scores
|
||||||
|
allScores := F.Pipe2(
|
||||||
|
scores,
|
||||||
|
TR.Traversal[string, int](),
|
||||||
|
T.GetAll[map[string]int, int],
|
||||||
|
)
|
||||||
|
// Result: [85, 92, 78] (order may vary)
|
||||||
|
|
||||||
|
// Increase all scores by 5
|
||||||
|
boosted := F.Pipe2(
|
||||||
|
scores,
|
||||||
|
TR.Traversal[string, int](),
|
||||||
|
T.Modify[map[string]int, int](func(score int) int {
|
||||||
|
return score + 5
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// Result: {"Alice": 90, "Bob": 97, "Charlie": 83}
|
||||||
|
|
||||||
|
# Working with Either Types
|
||||||
|
|
||||||
|
Traverse over the Right values:
|
||||||
|
|
||||||
|
import (
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
TE "github.com/IBM/fp-go/v2/optics/traversal/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
results := []E.Either[string, int]{
|
||||||
|
E.Right[string](10),
|
||||||
|
E.Left[int]("error"),
|
||||||
|
E.Right[string](20),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traversal for array of Either
|
||||||
|
arrayTrav := TA.Traversal[E.Either[string, int]]()
|
||||||
|
|
||||||
|
// Traversal for Right values
|
||||||
|
rightTrav := TE.Traversal[string, int]()
|
||||||
|
|
||||||
|
// Compose to access all Right values
|
||||||
|
allRightsTrav := F.Pipe1(
|
||||||
|
arrayTrav,
|
||||||
|
T.Compose[[]E.Either[string, int], E.Either[string, int], int, ...](rightTrav),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get all Right values
|
||||||
|
rights := T.GetAll(results)(allRightsTrav)
|
||||||
|
// Result: [10, 20]
|
||||||
|
|
||||||
|
// Double all Right values
|
||||||
|
doubled := F.Pipe2(
|
||||||
|
results,
|
||||||
|
allRightsTrav,
|
||||||
|
T.Modify[[]E.Either[string, int], int](func(n int) int { return n * 2 }),
|
||||||
|
)
|
||||||
|
// Result: [Right(20), Left("error"), Right(40)]
|
||||||
|
|
||||||
|
# Working with Option Types
|
||||||
|
|
||||||
|
Traverse over Some values:
|
||||||
|
|
||||||
|
import (
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
TO "github.com/IBM/fp-go/v2/optics/traversal/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
values := []O.Option[int]{
|
||||||
|
O.Some(1),
|
||||||
|
O.None[int](),
|
||||||
|
O.Some(2),
|
||||||
|
O.None[int](),
|
||||||
|
O.Some(3),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compose array and option traversals
|
||||||
|
allSomesTrav := F.Pipe1(
|
||||||
|
TA.Traversal[O.Option[int]](),
|
||||||
|
T.Compose[[]O.Option[int], O.Option[int], int, ...](TO.Traversal[int]()),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get all Some values
|
||||||
|
somes := T.GetAll(values)(allSomesTrav)
|
||||||
|
// Result: [1, 2, 3]
|
||||||
|
|
||||||
|
// Increment all Some values
|
||||||
|
incremented := F.Pipe2(
|
||||||
|
values,
|
||||||
|
allSomesTrav,
|
||||||
|
T.Modify[[]O.Option[int], int](func(n int) int { return n + 1 }),
|
||||||
|
)
|
||||||
|
// Result: [Some(2), None, Some(3), None, Some(4)]
|
||||||
|
|
||||||
|
# Real-World Example: Nested Data Structures
|
||||||
|
|
||||||
|
type Department struct {
|
||||||
|
Name string
|
||||||
|
Employees []Employee
|
||||||
|
}
|
||||||
|
|
||||||
|
type Employee struct {
|
||||||
|
Name string
|
||||||
|
Salary int
|
||||||
|
}
|
||||||
|
|
||||||
|
company := []Department{
|
||||||
|
{
|
||||||
|
Name: "Engineering",
|
||||||
|
Employees: []Employee{
|
||||||
|
{Name: "Alice", Salary: 100000},
|
||||||
|
{Name: "Bob", Salary: 95000},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Sales",
|
||||||
|
Employees: []Employee{
|
||||||
|
{Name: "Charlie", Salary: 80000},
|
||||||
|
{Name: "David", Salary: 85000},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traversal for departments
|
||||||
|
deptTrav := TA.Traversal[Department]()
|
||||||
|
|
||||||
|
// Traversal for employees within a department
|
||||||
|
empTrav := T.MakeTraversal(func(d Department) []Employee {
|
||||||
|
return d.Employees
|
||||||
|
})
|
||||||
|
|
||||||
|
// Traversal for employee array
|
||||||
|
empArrayTrav := TA.Traversal[Employee]()
|
||||||
|
|
||||||
|
// Compose to access all employees
|
||||||
|
allEmpTrav := F.Pipe2(
|
||||||
|
deptTrav,
|
||||||
|
T.Compose[[]Department, Department, []Employee, ...](empTrav),
|
||||||
|
T.Compose[[]Department, []Employee, Employee, ...](empArrayTrav),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get all employee names
|
||||||
|
names := F.Pipe2(
|
||||||
|
company,
|
||||||
|
allEmpTrav,
|
||||||
|
T.FoldMap[[]string, []Department, Employee](func(e Employee) []string {
|
||||||
|
return []string{e.Name}
|
||||||
|
}),
|
||||||
|
)(A.Monoid[string]())
|
||||||
|
// Result: ["Alice", "Bob", "Charlie", "David"]
|
||||||
|
|
||||||
|
// Give everyone a 10% raise
|
||||||
|
withRaises := F.Pipe2(
|
||||||
|
company,
|
||||||
|
allEmpTrav,
|
||||||
|
T.Modify[[]Department, Employee](func(e Employee) Employee {
|
||||||
|
e.Salary = int(float64(e.Salary) * 1.1)
|
||||||
|
return e
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Real-World Example: Filtering with Traversals
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
Name string
|
||||||
|
Price float64
|
||||||
|
InStock bool
|
||||||
|
}
|
||||||
|
|
||||||
|
products := []Product{
|
||||||
|
{Name: "Laptop", Price: 999.99, InStock: true},
|
||||||
|
{Name: "Mouse", Price: 29.99, InStock: false},
|
||||||
|
{Name: "Keyboard", Price: 79.99, InStock: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a traversal that only focuses on in-stock products
|
||||||
|
inStockTrav := T.MakeTraversal(func(ps []Product) []Product {
|
||||||
|
return A.Filter(func(p Product) bool {
|
||||||
|
return p.InStock
|
||||||
|
})(ps)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Apply discount to in-stock items
|
||||||
|
discounted := F.Pipe2(
|
||||||
|
products,
|
||||||
|
inStockTrav,
|
||||||
|
T.Modify[[]Product, Product](func(p Product) Product {
|
||||||
|
p.Price = p.Price * 0.9
|
||||||
|
return p
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// Only Laptop and Keyboard prices are reduced
|
||||||
|
|
||||||
|
# Real-World Example: Data Aggregation
|
||||||
|
|
||||||
|
type Order struct {
|
||||||
|
ID string
|
||||||
|
Items []OrderItem
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderItem struct {
|
||||||
|
Product string
|
||||||
|
Quantity int
|
||||||
|
Price float64
|
||||||
|
}
|
||||||
|
|
||||||
|
orders := []Order{
|
||||||
|
{
|
||||||
|
ID: "001",
|
||||||
|
Items: []OrderItem{
|
||||||
|
{Product: "Widget", Quantity: 2, Price: 10.0},
|
||||||
|
{Product: "Gadget", Quantity: 1, Price: 25.0},
|
||||||
|
},
|
||||||
|
Status: "completed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "002",
|
||||||
|
Items: []OrderItem{
|
||||||
|
{Product: "Widget", Quantity: 5, Price: 10.0},
|
||||||
|
},
|
||||||
|
Status: "completed",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traversal for orders
|
||||||
|
orderTrav := TA.Traversal[Order]()
|
||||||
|
|
||||||
|
// Traversal for items within an order
|
||||||
|
itemsTrav := T.MakeTraversal(func(o Order) []OrderItem {
|
||||||
|
return o.Items
|
||||||
|
})
|
||||||
|
|
||||||
|
// Traversal for item array
|
||||||
|
itemArrayTrav := TA.Traversal[OrderItem]()
|
||||||
|
|
||||||
|
// Compose to access all items
|
||||||
|
allItemsTrav := F.Pipe2(
|
||||||
|
orderTrav,
|
||||||
|
T.Compose[[]Order, Order, []OrderItem, ...](itemsTrav),
|
||||||
|
T.Compose[[]Order, []OrderItem, OrderItem, ...](itemArrayTrav),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Calculate total revenue
|
||||||
|
totalRevenue := F.Pipe2(
|
||||||
|
orders,
|
||||||
|
allItemsTrav,
|
||||||
|
T.FoldMap[float64, []Order, OrderItem](func(item OrderItem) float64 {
|
||||||
|
return float64(item.Quantity) * item.Price
|
||||||
|
}),
|
||||||
|
)(N.MonoidSum[float64]())
|
||||||
|
// Result: 95.0 (2*10 + 1*25 + 5*10)
|
||||||
|
|
||||||
|
# Traversals in the Optics Hierarchy
|
||||||
|
|
||||||
|
Traversals are the most general optic:
|
||||||
|
|
||||||
|
Iso[S, A]
|
||||||
|
↓
|
||||||
|
Lens[S, A]
|
||||||
|
↓
|
||||||
|
Optional[S, A]
|
||||||
|
↓
|
||||||
|
Traversal[S, A]
|
||||||
|
|
||||||
|
Prism[S, A]
|
||||||
|
↓
|
||||||
|
Optional[S, A]
|
||||||
|
↓
|
||||||
|
Traversal[S, A]
|
||||||
|
|
||||||
|
This means:
|
||||||
|
- Every Iso, Lens, Prism, and Optional can be converted to a Traversal
|
||||||
|
- Traversals are the most flexible but least specific optic
|
||||||
|
- Use more specific optics when possible for better type safety
|
||||||
|
|
||||||
|
# Performance Considerations
|
||||||
|
|
||||||
|
Traversals can be efficient but consider:
|
||||||
|
- Each traversal operation may iterate over all elements
|
||||||
|
- Composition creates nested iterations
|
||||||
|
- FoldMap is often more efficient than GetAll followed by reduction
|
||||||
|
- Modify creates new copies (immutability)
|
||||||
|
|
||||||
|
For best performance:
|
||||||
|
- Use specialized traversals (array, record, etc.) when available
|
||||||
|
- Avoid unnecessary composition
|
||||||
|
- Consider batch operations
|
||||||
|
- Cache composed traversals
|
||||||
|
|
||||||
|
# Type Safety
|
||||||
|
|
||||||
|
Traversals are fully type-safe:
|
||||||
|
- Compile-time type checking
|
||||||
|
- Generic type parameters ensure correctness
|
||||||
|
- Composition maintains type relationships
|
||||||
|
- No runtime type assertions
|
||||||
|
|
||||||
|
# Function Reference
|
||||||
|
|
||||||
|
Core Functions:
|
||||||
|
- Id: Create an identity traversal
|
||||||
|
- Modify: Apply a function to all focused values
|
||||||
|
- Set: Replace all focused values with a constant
|
||||||
|
- Compose: Compose two traversals
|
||||||
|
|
||||||
|
Aggregation:
|
||||||
|
- FoldMap: Map each value to a monoid and combine
|
||||||
|
- Fold: Fold over all values using a monoid
|
||||||
|
- GetAll: Collect all focused values into a list
|
||||||
|
|
||||||
|
# Specialized Traversals
|
||||||
|
|
||||||
|
The package includes specialized sub-packages for common patterns:
|
||||||
|
- array: Traversals for arrays and slices
|
||||||
|
- record: Traversals for maps
|
||||||
|
- either: Traversals for Either types
|
||||||
|
- option: Traversals for Option types
|
||||||
|
|
||||||
|
Each specialized package provides optimized implementations for its data type.
|
||||||
|
|
||||||
|
# Related Packages
|
||||||
|
|
||||||
|
- github.com/IBM/fp-go/v2/optics/lens: Lenses for single fields
|
||||||
|
- github.com/IBM/fp-go/v2/optics/prism: Prisms for sum types
|
||||||
|
- github.com/IBM/fp-go/v2/optics/optional: Optionals for maybe values
|
||||||
|
- github.com/IBM/fp-go/v2/optics/traversal/array: Array traversals
|
||||||
|
- github.com/IBM/fp-go/v2/optics/traversal/record: Record/map traversals
|
||||||
|
- github.com/IBM/fp-go/v2/optics/traversal/either: Either traversals
|
||||||
|
- github.com/IBM/fp-go/v2/optics/traversal/option: Option traversals
|
||||||
|
- github.com/IBM/fp-go/v2/array: Array utilities
|
||||||
|
- github.com/IBM/fp-go/v2/monoid: Monoid type class
|
||||||
|
*/
|
||||||
|
package traversal
|
||||||
|
|
||||||
|
// Made with Bob
|
||||||
@@ -47,6 +47,12 @@ type Company struct {
|
|||||||
Website *string
|
Website *string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fp-go:Lens
|
||||||
|
type CompanyExtended struct {
|
||||||
|
Company
|
||||||
|
Extended string
|
||||||
|
}
|
||||||
|
|
||||||
// fp-go:Lens
|
// fp-go:Lens
|
||||||
type CheckOption struct {
|
type CheckOption struct {
|
||||||
Name option.Option[string]
|
Name option.Option[string]
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ func TestPersonRefLensesOptionalIdempotent(t *testing.T) {
|
|||||||
refLenses := MakePersonRefLenses()
|
refLenses := MakePersonRefLenses()
|
||||||
|
|
||||||
// Test that setting Phone to the same value returns the same pointer
|
// Test that setting Phone to the same value returns the same pointer
|
||||||
samePhone := refLenses.Phone.Set(O.Some(&phoneValue))(person)
|
samePhone := refLenses.PhoneO.Set(O.Some(&phoneValue))(person)
|
||||||
assert.Same(t, person, samePhone, "Setting Phone to same value should return identical pointer")
|
assert.Same(t, person, samePhone, "Setting Phone to same value should return identical pointer")
|
||||||
|
|
||||||
// Test with Phone field set to nil
|
// Test with Phone field set to nil
|
||||||
@@ -226,24 +226,24 @@ func TestPersonRefLensesOptionalIdempotent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setting Phone to None when it's already nil should return same pointer
|
// Setting Phone to None when it's already nil should return same pointer
|
||||||
sameNilPhone := refLenses.Phone.Set(O.None[*string]())(personNoPhone)
|
sameNilPhone := refLenses.PhoneO.Set(O.None[*string]())(personNoPhone)
|
||||||
assert.Same(t, personNoPhone, sameNilPhone, "Setting Phone to None when already nil should return identical pointer")
|
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
|
// Test that setting to a different value creates a new pointer
|
||||||
newPhoneValue := "555-5678"
|
newPhoneValue := "555-5678"
|
||||||
differentPhone := refLenses.Phone.Set(O.Some(&newPhoneValue))(person)
|
differentPhone := refLenses.PhoneO.Set(O.Some(&newPhoneValue))(person)
|
||||||
assert.NotSame(t, person, differentPhone, "Setting Phone to different value should return new pointer")
|
assert.NotSame(t, person, differentPhone, "Setting Phone to different value should return new pointer")
|
||||||
assert.Equal(t, &newPhoneValue, differentPhone.Phone)
|
assert.Equal(t, &newPhoneValue, differentPhone.Phone)
|
||||||
assert.Equal(t, &phoneValue, person.Phone, "Original should be unchanged")
|
assert.Equal(t, &phoneValue, person.Phone, "Original should be unchanged")
|
||||||
|
|
||||||
// Test setting from nil to Some creates new pointer
|
// Test setting from nil to Some creates new pointer
|
||||||
somePhone := refLenses.Phone.Set(O.Some(&phoneValue))(personNoPhone)
|
somePhone := refLenses.PhoneO.Set(O.Some(&phoneValue))(personNoPhone)
|
||||||
assert.NotSame(t, personNoPhone, somePhone, "Setting Phone from nil to Some should return new pointer")
|
assert.NotSame(t, personNoPhone, somePhone, "Setting Phone from nil to Some should return new pointer")
|
||||||
assert.Equal(t, &phoneValue, somePhone.Phone)
|
assert.Equal(t, &phoneValue, somePhone.Phone)
|
||||||
assert.Nil(t, personNoPhone.Phone, "Original should be unchanged")
|
assert.Nil(t, personNoPhone.Phone, "Original should be unchanged")
|
||||||
|
|
||||||
// Test setting from Some to None creates new pointer
|
// Test setting from Some to None creates new pointer
|
||||||
nonePhone := refLenses.Phone.Set(O.None[*string]())(person)
|
nonePhone := refLenses.PhoneO.Set(O.None[*string]())(person)
|
||||||
assert.NotSame(t, person, nonePhone, "Setting Phone from Some to None should return new pointer")
|
assert.NotSame(t, person, nonePhone, "Setting Phone from Some to None should return new pointer")
|
||||||
assert.Nil(t, nonePhone.Phone)
|
assert.Nil(t, nonePhone.Phone)
|
||||||
assert.Equal(t, &phoneValue, person.Phone, "Original should be unchanged")
|
assert.Equal(t, &phoneValue, person.Phone, "Original should be unchanged")
|
||||||
@@ -264,7 +264,7 @@ func TestAddressRefLensesOptionalIdempotent(t *testing.T) {
|
|||||||
refLenses := MakeAddressRefLenses()
|
refLenses := MakeAddressRefLenses()
|
||||||
|
|
||||||
// Test that setting State to the same value returns the same pointer
|
// Test that setting State to the same value returns the same pointer
|
||||||
sameState := refLenses.State.Set(O.Some(&stateValue))(address)
|
sameState := refLenses.StateO.Set(O.Some(&stateValue))(address)
|
||||||
assert.Same(t, address, sameState, "Setting State to same value should return identical pointer")
|
assert.Same(t, address, sameState, "Setting State to same value should return identical pointer")
|
||||||
|
|
||||||
// Test with State field set to nil
|
// Test with State field set to nil
|
||||||
@@ -277,12 +277,12 @@ func TestAddressRefLensesOptionalIdempotent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setting State to None when it's already nil should return same pointer
|
// Setting State to None when it's already nil should return same pointer
|
||||||
sameNilState := refLenses.State.Set(O.None[*string]())(addressNoState)
|
sameNilState := refLenses.StateO.Set(O.None[*string]())(addressNoState)
|
||||||
assert.Same(t, addressNoState, sameNilState, "Setting State to None when already nil should return identical pointer")
|
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
|
// Test that setting to a different value creates a new pointer
|
||||||
newStateValue := "New York"
|
newStateValue := "New York"
|
||||||
differentState := refLenses.State.Set(O.Some(&newStateValue))(address)
|
differentState := refLenses.StateO.Set(O.Some(&newStateValue))(address)
|
||||||
assert.NotSame(t, address, differentState, "Setting State to different value should return new pointer")
|
assert.NotSame(t, address, differentState, "Setting State to different value should return new pointer")
|
||||||
assert.Equal(t, &newStateValue, differentState.State)
|
assert.Equal(t, &newStateValue, differentState.State)
|
||||||
assert.Equal(t, &stateValue, address.State, "Original should be unchanged")
|
assert.Equal(t, &stateValue, address.State, "Original should be unchanged")
|
||||||
@@ -311,7 +311,7 @@ func TestCompanyRefLensesOptionalIdempotent(t *testing.T) {
|
|||||||
refLenses := MakeCompanyRefLenses()
|
refLenses := MakeCompanyRefLenses()
|
||||||
|
|
||||||
// Test that setting Website to the same value returns the same pointer
|
// Test that setting Website to the same value returns the same pointer
|
||||||
sameWebsite := refLenses.Website.Set(O.Some(&websiteValue))(company)
|
sameWebsite := refLenses.WebsiteO.Set(O.Some(&websiteValue))(company)
|
||||||
assert.Same(t, company, sameWebsite, "Setting Website to same value should return identical pointer")
|
assert.Same(t, company, sameWebsite, "Setting Website to same value should return identical pointer")
|
||||||
|
|
||||||
// Test with Website field set to nil
|
// Test with Website field set to nil
|
||||||
@@ -331,12 +331,12 @@ func TestCompanyRefLensesOptionalIdempotent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setting Website to None when it's already nil should return same pointer
|
// Setting Website to None when it's already nil should return same pointer
|
||||||
sameNilWebsite := refLenses.Website.Set(O.None[*string]())(companyNoWebsite)
|
sameNilWebsite := refLenses.WebsiteO.Set(O.None[*string]())(companyNoWebsite)
|
||||||
assert.Same(t, companyNoWebsite, sameNilWebsite, "Setting Website to None when already nil should return identical pointer")
|
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
|
// Test that setting to a different value creates a new pointer
|
||||||
newWebsiteValue := "https://newsite.com"
|
newWebsiteValue := "https://newsite.com"
|
||||||
differentWebsite := refLenses.Website.Set(O.Some(&newWebsiteValue))(company)
|
differentWebsite := refLenses.WebsiteO.Set(O.Some(&newWebsiteValue))(company)
|
||||||
assert.NotSame(t, company, differentWebsite, "Setting Website to different value should return new pointer")
|
assert.NotSame(t, company, differentWebsite, "Setting Website to different value should return new pointer")
|
||||||
assert.Equal(t, &newWebsiteValue, differentWebsite.Website)
|
assert.Equal(t, &newWebsiteValue, differentWebsite.Website)
|
||||||
assert.Equal(t, &websiteValue, company.Website, "Original should be unchanged")
|
assert.Equal(t, &websiteValue, company.Website, "Original should be unchanged")
|
||||||
|
|||||||
@@ -2,246 +2,459 @@ 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 18:15:07.69943 +0100 CET m=+0.005345401
|
// 2025-11-13 09:05:02.7405925 +0100 CET m=+0.003158301
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
IO "github.com/IBM/fp-go/v2/optics/iso/option"
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
LO "github.com/IBM/fp-go/v2/optics/lens/option"
|
LO "github.com/IBM/fp-go/v2/optics/lens/option"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
I "github.com/IBM/fp-go/v2/optics/iso/option"
|
|
||||||
option "github.com/IBM/fp-go/v2/optics/lens/option"
|
option "github.com/IBM/fp-go/v2/optics/lens/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PersonLenses provides lenses for accessing fields of Person
|
// PersonLenses provides lenses for accessing fields of Person
|
||||||
type PersonLenses struct {
|
type PersonLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Name L.Lens[Person, string]
|
Name L.Lens[Person, string]
|
||||||
Age L.Lens[Person, int]
|
Age L.Lens[Person, int]
|
||||||
Email L.Lens[Person, string]
|
Email L.Lens[Person, string]
|
||||||
Phone LO.LensO[Person, *string]
|
Phone L.Lens[Person, *string]
|
||||||
|
// optional fields
|
||||||
|
NameO LO.LensO[Person, string]
|
||||||
|
AgeO LO.LensO[Person, int]
|
||||||
|
EmailO LO.LensO[Person, string]
|
||||||
|
PhoneO LO.LensO[Person, *string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// PersonRefLenses provides lenses for accessing fields of Person via a reference to Person
|
// PersonRefLenses provides lenses for accessing fields of Person via a reference to Person
|
||||||
type PersonRefLenses struct {
|
type PersonRefLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Name L.Lens[*Person, string]
|
Name L.Lens[*Person, string]
|
||||||
Age L.Lens[*Person, int]
|
Age L.Lens[*Person, int]
|
||||||
Email L.Lens[*Person, string]
|
Email L.Lens[*Person, string]
|
||||||
Phone LO.LensO[*Person, *string]
|
Phone L.Lens[*Person, *string]
|
||||||
|
// optional fields
|
||||||
|
NameO LO.LensO[*Person, string]
|
||||||
|
AgeO LO.LensO[*Person, int]
|
||||||
|
EmailO LO.LensO[*Person, string]
|
||||||
|
PhoneO LO.LensO[*Person, *string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakePersonLenses creates a new PersonLenses with lenses for all fields
|
// MakePersonLenses creates a new PersonLenses with lenses for all fields
|
||||||
func MakePersonLenses() PersonLenses {
|
func MakePersonLenses() PersonLenses {
|
||||||
isoPhone := I.FromZero[*string]()
|
// mandatory lenses
|
||||||
return PersonLenses{
|
lensName := L.MakeLens(
|
||||||
Name: L.MakeLens(
|
|
||||||
func(s Person) string { return s.Name },
|
func(s Person) string { return s.Name },
|
||||||
func(s Person, v string) Person { s.Name = v; return s },
|
func(s Person, v string) Person { s.Name = v; return s },
|
||||||
),
|
)
|
||||||
Age: L.MakeLens(
|
lensAge := L.MakeLens(
|
||||||
func(s Person) int { return s.Age },
|
func(s Person) int { return s.Age },
|
||||||
func(s Person, v int) Person { s.Age = v; return s },
|
func(s Person, v int) Person { s.Age = v; return s },
|
||||||
),
|
)
|
||||||
Email: L.MakeLens(
|
lensEmail := L.MakeLens(
|
||||||
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.MakeLens(
|
lensPhone := L.MakeLens(
|
||||||
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 },
|
||||||
),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensNameO := LO.FromIso[Person](IO.FromZero[string]())(lensName)
|
||||||
|
lensAgeO := LO.FromIso[Person](IO.FromZero[int]())(lensAge)
|
||||||
|
lensEmailO := LO.FromIso[Person](IO.FromZero[string]())(lensEmail)
|
||||||
|
lensPhoneO := LO.FromIso[Person](IO.FromZero[*string]())(lensPhone)
|
||||||
|
return PersonLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Name: lensName,
|
||||||
|
Age: lensAge,
|
||||||
|
Email: lensEmail,
|
||||||
|
Phone: lensPhone,
|
||||||
|
// optional lenses
|
||||||
|
NameO: lensNameO,
|
||||||
|
AgeO: lensAgeO,
|
||||||
|
EmailO: lensEmailO,
|
||||||
|
PhoneO: lensPhoneO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
return PersonRefLenses{
|
// mandatory lenses
|
||||||
Name: L.MakeLensStrict(
|
lensName := L.MakeLensStrict(
|
||||||
func(s *Person) string { return s.Name },
|
func(s *Person) string { return s.Name },
|
||||||
func(s *Person, v string) *Person { s.Name = v; return s },
|
func(s *Person, v string) *Person { s.Name = v; return s },
|
||||||
),
|
)
|
||||||
Age: L.MakeLensStrict(
|
lensAge := L.MakeLensStrict(
|
||||||
func(s *Person) int { return s.Age },
|
func(s *Person) int { return s.Age },
|
||||||
func(s *Person, v int) *Person { s.Age = v; return s },
|
func(s *Person, v int) *Person { s.Age = v; return s },
|
||||||
),
|
)
|
||||||
Email: L.MakeLensStrict(
|
lensEmail := L.MakeLensStrict(
|
||||||
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: LO.FromIso[*Person](I.FromZero[*string]())(L.MakeLensStrict(
|
lensPhone := L.MakeLensStrict(
|
||||||
func(s *Person) *string { return s.Phone },
|
func(s *Person) *string { return s.Phone },
|
||||||
func(s *Person, v *string) *Person { s.Phone = v; return s },
|
func(s *Person, v *string) *Person { s.Phone = v; return s },
|
||||||
)),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensNameO := LO.FromIso[*Person](IO.FromZero[string]())(lensName)
|
||||||
|
lensAgeO := LO.FromIso[*Person](IO.FromZero[int]())(lensAge)
|
||||||
|
lensEmailO := LO.FromIso[*Person](IO.FromZero[string]())(lensEmail)
|
||||||
|
lensPhoneO := LO.FromIso[*Person](IO.FromZero[*string]())(lensPhone)
|
||||||
|
return PersonRefLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Name: lensName,
|
||||||
|
Age: lensAge,
|
||||||
|
Email: lensEmail,
|
||||||
|
Phone: lensPhone,
|
||||||
|
// optional lenses
|
||||||
|
NameO: lensNameO,
|
||||||
|
AgeO: lensAgeO,
|
||||||
|
EmailO: lensEmailO,
|
||||||
|
PhoneO: lensPhoneO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddressLenses provides lenses for accessing fields of Address
|
// AddressLenses provides lenses for accessing fields of Address
|
||||||
type AddressLenses struct {
|
type AddressLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Street L.Lens[Address, string]
|
Street L.Lens[Address, string]
|
||||||
City L.Lens[Address, string]
|
City L.Lens[Address, string]
|
||||||
ZipCode L.Lens[Address, string]
|
ZipCode L.Lens[Address, string]
|
||||||
Country L.Lens[Address, string]
|
Country L.Lens[Address, string]
|
||||||
State LO.LensO[Address, *string]
|
State L.Lens[Address, *string]
|
||||||
|
// optional fields
|
||||||
|
StreetO LO.LensO[Address, string]
|
||||||
|
CityO LO.LensO[Address, string]
|
||||||
|
ZipCodeO LO.LensO[Address, string]
|
||||||
|
CountryO LO.LensO[Address, string]
|
||||||
|
StateO LO.LensO[Address, *string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddressRefLenses provides lenses for accessing fields of Address via a reference to Address
|
// AddressRefLenses provides lenses for accessing fields of Address via a reference to Address
|
||||||
type AddressRefLenses struct {
|
type AddressRefLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Street L.Lens[*Address, string]
|
Street L.Lens[*Address, string]
|
||||||
City L.Lens[*Address, string]
|
City L.Lens[*Address, string]
|
||||||
ZipCode L.Lens[*Address, string]
|
ZipCode L.Lens[*Address, string]
|
||||||
Country L.Lens[*Address, string]
|
Country L.Lens[*Address, string]
|
||||||
State LO.LensO[*Address, *string]
|
State L.Lens[*Address, *string]
|
||||||
|
// optional fields
|
||||||
|
StreetO LO.LensO[*Address, string]
|
||||||
|
CityO LO.LensO[*Address, string]
|
||||||
|
ZipCodeO LO.LensO[*Address, string]
|
||||||
|
CountryO LO.LensO[*Address, string]
|
||||||
|
StateO LO.LensO[*Address, *string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeAddressLenses creates a new AddressLenses with lenses for all fields
|
// MakeAddressLenses creates a new AddressLenses with lenses for all fields
|
||||||
func MakeAddressLenses() AddressLenses {
|
func MakeAddressLenses() AddressLenses {
|
||||||
isoState := I.FromZero[*string]()
|
// mandatory lenses
|
||||||
return AddressLenses{
|
lensStreet := L.MakeLens(
|
||||||
Street: L.MakeLens(
|
|
||||||
func(s Address) string { return s.Street },
|
func(s Address) string { return s.Street },
|
||||||
func(s Address, v string) Address { s.Street = v; return s },
|
func(s Address, v string) Address { s.Street = v; return s },
|
||||||
),
|
)
|
||||||
City: L.MakeLens(
|
lensCity := L.MakeLens(
|
||||||
func(s Address) string { return s.City },
|
func(s Address) string { return s.City },
|
||||||
func(s Address, v string) Address { s.City = v; return s },
|
func(s Address, v string) Address { s.City = v; return s },
|
||||||
),
|
)
|
||||||
ZipCode: L.MakeLens(
|
lensZipCode := L.MakeLens(
|
||||||
func(s Address) string { return s.ZipCode },
|
func(s Address) string { return s.ZipCode },
|
||||||
func(s Address, v string) Address { s.ZipCode = v; return s },
|
func(s Address, v string) Address { s.ZipCode = v; return s },
|
||||||
),
|
)
|
||||||
Country: L.MakeLens(
|
lensCountry := L.MakeLens(
|
||||||
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.MakeLens(
|
lensState := L.MakeLens(
|
||||||
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 },
|
||||||
),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensStreetO := LO.FromIso[Address](IO.FromZero[string]())(lensStreet)
|
||||||
|
lensCityO := LO.FromIso[Address](IO.FromZero[string]())(lensCity)
|
||||||
|
lensZipCodeO := LO.FromIso[Address](IO.FromZero[string]())(lensZipCode)
|
||||||
|
lensCountryO := LO.FromIso[Address](IO.FromZero[string]())(lensCountry)
|
||||||
|
lensStateO := LO.FromIso[Address](IO.FromZero[*string]())(lensState)
|
||||||
|
return AddressLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Street: lensStreet,
|
||||||
|
City: lensCity,
|
||||||
|
ZipCode: lensZipCode,
|
||||||
|
Country: lensCountry,
|
||||||
|
State: lensState,
|
||||||
|
// optional lenses
|
||||||
|
StreetO: lensStreetO,
|
||||||
|
CityO: lensCityO,
|
||||||
|
ZipCodeO: lensZipCodeO,
|
||||||
|
CountryO: lensCountryO,
|
||||||
|
StateO: lensStateO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
return AddressRefLenses{
|
// mandatory lenses
|
||||||
Street: L.MakeLensStrict(
|
lensStreet := L.MakeLensStrict(
|
||||||
func(s *Address) string { return s.Street },
|
func(s *Address) string { return s.Street },
|
||||||
func(s *Address, v string) *Address { s.Street = v; return s },
|
func(s *Address, v string) *Address { s.Street = v; return s },
|
||||||
),
|
)
|
||||||
City: L.MakeLensStrict(
|
lensCity := L.MakeLensStrict(
|
||||||
func(s *Address) string { return s.City },
|
func(s *Address) string { return s.City },
|
||||||
func(s *Address, v string) *Address { s.City = v; return s },
|
func(s *Address, v string) *Address { s.City = v; return s },
|
||||||
),
|
)
|
||||||
ZipCode: L.MakeLensStrict(
|
lensZipCode := L.MakeLensStrict(
|
||||||
func(s *Address) string { return s.ZipCode },
|
func(s *Address) string { return s.ZipCode },
|
||||||
func(s *Address, v string) *Address { s.ZipCode = v; return s },
|
func(s *Address, v string) *Address { s.ZipCode = v; return s },
|
||||||
),
|
)
|
||||||
Country: L.MakeLensStrict(
|
lensCountry := L.MakeLensStrict(
|
||||||
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: LO.FromIso[*Address](I.FromZero[*string]())(L.MakeLensStrict(
|
lensState := L.MakeLensStrict(
|
||||||
func(s *Address) *string { return s.State },
|
func(s *Address) *string { return s.State },
|
||||||
func(s *Address, v *string) *Address { s.State = v; return s },
|
func(s *Address, v *string) *Address { s.State = v; return s },
|
||||||
)),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensStreetO := LO.FromIso[*Address](IO.FromZero[string]())(lensStreet)
|
||||||
|
lensCityO := LO.FromIso[*Address](IO.FromZero[string]())(lensCity)
|
||||||
|
lensZipCodeO := LO.FromIso[*Address](IO.FromZero[string]())(lensZipCode)
|
||||||
|
lensCountryO := LO.FromIso[*Address](IO.FromZero[string]())(lensCountry)
|
||||||
|
lensStateO := LO.FromIso[*Address](IO.FromZero[*string]())(lensState)
|
||||||
|
return AddressRefLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Street: lensStreet,
|
||||||
|
City: lensCity,
|
||||||
|
ZipCode: lensZipCode,
|
||||||
|
Country: lensCountry,
|
||||||
|
State: lensState,
|
||||||
|
// optional lenses
|
||||||
|
StreetO: lensStreetO,
|
||||||
|
CityO: lensCityO,
|
||||||
|
ZipCodeO: lensZipCodeO,
|
||||||
|
CountryO: lensCountryO,
|
||||||
|
StateO: lensStateO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompanyLenses provides lenses for accessing fields of Company
|
// CompanyLenses provides lenses for accessing fields of Company
|
||||||
type CompanyLenses struct {
|
type CompanyLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Name L.Lens[Company, string]
|
Name L.Lens[Company, string]
|
||||||
Address L.Lens[Company, Address]
|
Address L.Lens[Company, Address]
|
||||||
CEO L.Lens[Company, Person]
|
CEO L.Lens[Company, Person]
|
||||||
Website LO.LensO[Company, *string]
|
Website L.Lens[Company, *string]
|
||||||
|
// optional fields
|
||||||
|
NameO LO.LensO[Company, string]
|
||||||
|
AddressO LO.LensO[Company, Address]
|
||||||
|
CEOO LO.LensO[Company, Person]
|
||||||
|
WebsiteO LO.LensO[Company, *string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompanyRefLenses provides lenses for accessing fields of Company via a reference to Company
|
// CompanyRefLenses provides lenses for accessing fields of Company via a reference to Company
|
||||||
type CompanyRefLenses struct {
|
type CompanyRefLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Name L.Lens[*Company, string]
|
Name L.Lens[*Company, string]
|
||||||
Address L.Lens[*Company, Address]
|
Address L.Lens[*Company, Address]
|
||||||
CEO L.Lens[*Company, Person]
|
CEO L.Lens[*Company, Person]
|
||||||
Website LO.LensO[*Company, *string]
|
Website L.Lens[*Company, *string]
|
||||||
|
// optional fields
|
||||||
|
NameO LO.LensO[*Company, string]
|
||||||
|
AddressO LO.LensO[*Company, Address]
|
||||||
|
CEOO LO.LensO[*Company, Person]
|
||||||
|
WebsiteO LO.LensO[*Company, *string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeCompanyLenses creates a new CompanyLenses with lenses for all fields
|
// MakeCompanyLenses creates a new CompanyLenses with lenses for all fields
|
||||||
func MakeCompanyLenses() CompanyLenses {
|
func MakeCompanyLenses() CompanyLenses {
|
||||||
isoWebsite := I.FromZero[*string]()
|
// mandatory lenses
|
||||||
return CompanyLenses{
|
lensName := L.MakeLens(
|
||||||
Name: L.MakeLens(
|
|
||||||
func(s Company) string { return s.Name },
|
func(s Company) string { return s.Name },
|
||||||
func(s Company, v string) Company { s.Name = v; return s },
|
func(s Company, v string) Company { s.Name = v; return s },
|
||||||
),
|
)
|
||||||
Address: L.MakeLens(
|
lensAddress := L.MakeLens(
|
||||||
func(s Company) Address { return s.Address },
|
func(s Company) Address { return s.Address },
|
||||||
func(s Company, v Address) Company { s.Address = v; return s },
|
func(s Company, v Address) Company { s.Address = v; return s },
|
||||||
),
|
)
|
||||||
CEO: L.MakeLens(
|
lensCEO := L.MakeLens(
|
||||||
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.MakeLens(
|
lensWebsite := L.MakeLens(
|
||||||
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 },
|
||||||
),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensNameO := LO.FromIso[Company](IO.FromZero[string]())(lensName)
|
||||||
|
lensAddressO := LO.FromIso[Company](IO.FromZero[Address]())(lensAddress)
|
||||||
|
lensCEOO := LO.FromIso[Company](IO.FromZero[Person]())(lensCEO)
|
||||||
|
lensWebsiteO := LO.FromIso[Company](IO.FromZero[*string]())(lensWebsite)
|
||||||
|
return CompanyLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Name: lensName,
|
||||||
|
Address: lensAddress,
|
||||||
|
CEO: lensCEO,
|
||||||
|
Website: lensWebsite,
|
||||||
|
// optional lenses
|
||||||
|
NameO: lensNameO,
|
||||||
|
AddressO: lensAddressO,
|
||||||
|
CEOO: lensCEOO,
|
||||||
|
WebsiteO: lensWebsiteO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
return CompanyRefLenses{
|
// mandatory lenses
|
||||||
Name: L.MakeLensStrict(
|
lensName := L.MakeLensStrict(
|
||||||
func(s *Company) string { return s.Name },
|
func(s *Company) string { return s.Name },
|
||||||
func(s *Company, v string) *Company { s.Name = v; return s },
|
func(s *Company, v string) *Company { s.Name = v; return s },
|
||||||
),
|
)
|
||||||
Address: L.MakeLensStrict(
|
lensAddress := L.MakeLensStrict(
|
||||||
func(s *Company) Address { return s.Address },
|
func(s *Company) Address { return s.Address },
|
||||||
func(s *Company, v Address) *Company { s.Address = v; return s },
|
func(s *Company, v Address) *Company { s.Address = v; return s },
|
||||||
),
|
)
|
||||||
CEO: L.MakeLensStrict(
|
lensCEO := L.MakeLensStrict(
|
||||||
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: LO.FromIso[*Company](I.FromZero[*string]())(L.MakeLensStrict(
|
lensWebsite := L.MakeLensStrict(
|
||||||
func(s *Company) *string { return s.Website },
|
func(s *Company) *string { return s.Website },
|
||||||
func(s *Company, v *string) *Company { s.Website = v; return s },
|
func(s *Company, v *string) *Company { s.Website = v; return s },
|
||||||
)),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensNameO := LO.FromIso[*Company](IO.FromZero[string]())(lensName)
|
||||||
|
lensAddressO := LO.FromIso[*Company](IO.FromZero[Address]())(lensAddress)
|
||||||
|
lensCEOO := LO.FromIso[*Company](IO.FromZero[Person]())(lensCEO)
|
||||||
|
lensWebsiteO := LO.FromIso[*Company](IO.FromZero[*string]())(lensWebsite)
|
||||||
|
return CompanyRefLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Name: lensName,
|
||||||
|
Address: lensAddress,
|
||||||
|
CEO: lensCEO,
|
||||||
|
Website: lensWebsite,
|
||||||
|
// optional lenses
|
||||||
|
NameO: lensNameO,
|
||||||
|
AddressO: lensAddressO,
|
||||||
|
CEOO: lensCEOO,
|
||||||
|
WebsiteO: lensWebsiteO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompanyExtendedLenses provides lenses for accessing fields of CompanyExtended
|
||||||
|
type CompanyExtendedLenses struct {
|
||||||
|
// mandatory fields
|
||||||
|
Extended L.Lens[CompanyExtended, string]
|
||||||
|
// optional fields
|
||||||
|
ExtendedO LO.LensO[CompanyExtended, string]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompanyExtendedRefLenses provides lenses for accessing fields of CompanyExtended via a reference to CompanyExtended
|
||||||
|
type CompanyExtendedRefLenses struct {
|
||||||
|
// mandatory fields
|
||||||
|
Extended L.Lens[*CompanyExtended, string]
|
||||||
|
// optional fields
|
||||||
|
ExtendedO LO.LensO[*CompanyExtended, string]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCompanyExtendedLenses creates a new CompanyExtendedLenses with lenses for all fields
|
||||||
|
func MakeCompanyExtendedLenses() CompanyExtendedLenses {
|
||||||
|
// mandatory lenses
|
||||||
|
lensExtended := L.MakeLens(
|
||||||
|
func(s CompanyExtended) string { return s.Extended },
|
||||||
|
func(s CompanyExtended, v string) CompanyExtended { s.Extended = v; return s },
|
||||||
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensExtendedO := LO.FromIso[CompanyExtended](IO.FromZero[string]())(lensExtended)
|
||||||
|
return CompanyExtendedLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Extended: lensExtended,
|
||||||
|
// optional lenses
|
||||||
|
ExtendedO: lensExtendedO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCompanyExtendedRefLenses creates a new CompanyExtendedRefLenses with lenses for all fields
|
||||||
|
func MakeCompanyExtendedRefLenses() CompanyExtendedRefLenses {
|
||||||
|
// mandatory lenses
|
||||||
|
lensExtended := L.MakeLensStrict(
|
||||||
|
func(s *CompanyExtended) string { return s.Extended },
|
||||||
|
func(s *CompanyExtended, v string) *CompanyExtended { s.Extended = v; return s },
|
||||||
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensExtendedO := LO.FromIso[*CompanyExtended](IO.FromZero[string]())(lensExtended)
|
||||||
|
return CompanyExtendedRefLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Extended: lensExtended,
|
||||||
|
// optional lenses
|
||||||
|
ExtendedO: lensExtendedO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckOptionLenses provides lenses for accessing fields of CheckOption
|
// CheckOptionLenses provides lenses for accessing fields of CheckOption
|
||||||
type CheckOptionLenses struct {
|
type CheckOptionLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Name L.Lens[CheckOption, option.Option[string]]
|
Name L.Lens[CheckOption, option.Option[string]]
|
||||||
Value LO.LensO[CheckOption, string]
|
Value L.Lens[CheckOption, string]
|
||||||
|
// optional fields
|
||||||
|
NameO LO.LensO[CheckOption, option.Option[string]]
|
||||||
|
ValueO LO.LensO[CheckOption, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckOptionRefLenses provides lenses for accessing fields of CheckOption via a reference to CheckOption
|
// CheckOptionRefLenses provides lenses for accessing fields of CheckOption via a reference to CheckOption
|
||||||
type CheckOptionRefLenses struct {
|
type CheckOptionRefLenses struct {
|
||||||
|
// mandatory fields
|
||||||
Name L.Lens[*CheckOption, option.Option[string]]
|
Name L.Lens[*CheckOption, option.Option[string]]
|
||||||
Value LO.LensO[*CheckOption, string]
|
Value L.Lens[*CheckOption, string]
|
||||||
|
// optional fields
|
||||||
|
NameO LO.LensO[*CheckOption, option.Option[string]]
|
||||||
|
ValueO LO.LensO[*CheckOption, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeCheckOptionLenses creates a new CheckOptionLenses with lenses for all fields
|
// MakeCheckOptionLenses creates a new CheckOptionLenses with lenses for all fields
|
||||||
func MakeCheckOptionLenses() CheckOptionLenses {
|
func MakeCheckOptionLenses() CheckOptionLenses {
|
||||||
isoValue := I.FromZero[string]()
|
// mandatory lenses
|
||||||
return CheckOptionLenses{
|
lensName := L.MakeLens(
|
||||||
Name: L.MakeLens(
|
|
||||||
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.MakeLens(
|
lensValue := L.MakeLens(
|
||||||
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 },
|
||||||
),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensNameO := LO.FromIso[CheckOption](IO.FromZero[option.Option[string]]())(lensName)
|
||||||
|
lensValueO := LO.FromIso[CheckOption](IO.FromZero[string]())(lensValue)
|
||||||
|
return CheckOptionLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Name: lensName,
|
||||||
|
Value: lensValue,
|
||||||
|
// optional lenses
|
||||||
|
NameO: lensNameO,
|
||||||
|
ValueO: lensValueO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
return CheckOptionRefLenses{
|
// mandatory lenses
|
||||||
Name: L.MakeLensRef(
|
lensName := 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: LO.FromIso[*CheckOption](I.FromZero[string]())(L.MakeLensStrict(
|
lensValue := L.MakeLensStrict(
|
||||||
func(s *CheckOption) string { return s.Value },
|
func(s *CheckOption) string { return s.Value },
|
||||||
func(s *CheckOption, v string) *CheckOption { s.Value = v; return s },
|
func(s *CheckOption, v string) *CheckOption { s.Value = v; return s },
|
||||||
)),
|
)
|
||||||
|
// optional lenses
|
||||||
|
lensNameO := LO.FromIso[*CheckOption](IO.FromZero[option.Option[string]]())(lensName)
|
||||||
|
lensValueO := LO.FromIso[*CheckOption](IO.FromZero[string]())(lensValue)
|
||||||
|
return CheckOptionRefLenses{
|
||||||
|
// mandatory lenses
|
||||||
|
Name: lensName,
|
||||||
|
Value: lensValue,
|
||||||
|
// optional lenses
|
||||||
|
NameO: lensNameO,
|
||||||
|
ValueO: lensValueO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user