mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
* fix: initial checkin of v2 Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: slowly migrate IO Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: migrate MonadTraverseArray and TraverseArray Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: migrate traversal Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: complete migration of IO Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: migrate ioeither Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: refactorY Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: next step in migration Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: adjust IO generation code Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: get rid of more IO methods Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: get rid of more IO * fix: convert iooption Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: convert reader Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: convert a bit of reader Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: new build script Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: cleanup Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: reformat Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: simplify Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: some cleanup Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: adjust Pair to Haskell semantic Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: documentation and testcases Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: some performance optimizations Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: remove coverage Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: better doc Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> --------- Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
272 lines
6.8 KiB
Go
272 lines
6.8 KiB
Go
// 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 identity implements the Identity monad, the simplest possible monad.
|
|
|
|
# Overview
|
|
|
|
The Identity monad is a trivial monad that simply wraps a value without adding
|
|
any computational context. It's the identity element in the category of monads,
|
|
meaning it doesn't add any effects or behavior - it just passes values through.
|
|
|
|
While seemingly useless, the Identity monad serves several important purposes:
|
|
- As a baseline for understanding more complex monads
|
|
- For testing monad transformers
|
|
- As a default when no specific monad is needed
|
|
- For generic code that works with any monad
|
|
|
|
In this implementation, Identity[A] is simply represented as type A itself,
|
|
making it a zero-cost abstraction.
|
|
|
|
# Core Concepts
|
|
|
|
The Identity monad implements the standard monadic operations:
|
|
|
|
- Of: Wraps a value (identity function)
|
|
- Map: Transforms the wrapped value
|
|
- Chain (FlatMap): Chains computations
|
|
- Ap: Applies a wrapped function to a wrapped value
|
|
|
|
Since Identity adds no context, all these operations reduce to simple function
|
|
application.
|
|
|
|
# Basic Usage
|
|
|
|
Creating and transforming Identity values:
|
|
|
|
// Of wraps a value (but it's just the identity)
|
|
x := identity.Of(42)
|
|
// x is just 42
|
|
|
|
// Map transforms the value
|
|
doubled := identity.Map(func(n int) int {
|
|
return n * 2
|
|
})(x)
|
|
// doubled is 84
|
|
|
|
// Chain for monadic composition
|
|
result := identity.Chain(func(n int) int {
|
|
return n + 10
|
|
})(doubled)
|
|
// result is 94
|
|
|
|
# Functor Operations
|
|
|
|
Map transforms values:
|
|
|
|
import F "github.com/IBM/fp-go/v2/function"
|
|
|
|
// Simple mapping
|
|
result := F.Pipe1(
|
|
5,
|
|
identity.Map(func(n int) int { return n * n }),
|
|
)
|
|
// result is 25
|
|
|
|
// MapTo replaces with a constant
|
|
result := F.Pipe1(
|
|
"ignored",
|
|
identity.MapTo[string, int](100),
|
|
)
|
|
// result is 100
|
|
|
|
# Applicative Operations
|
|
|
|
Ap applies wrapped functions:
|
|
|
|
add := func(a int) func(int) int {
|
|
return func(b int) int {
|
|
return a + b
|
|
}
|
|
}
|
|
|
|
// Apply a curried function
|
|
result := F.Pipe1(
|
|
add(10),
|
|
identity.Ap[int, int](5),
|
|
)
|
|
// result is 15
|
|
|
|
# Monad Operations
|
|
|
|
Chain for sequential composition:
|
|
|
|
// Chain multiple operations
|
|
result := F.Pipe2(
|
|
10,
|
|
identity.Chain(func(n int) int { return n * 2 }),
|
|
identity.Chain(func(n int) int { return n + 5 }),
|
|
)
|
|
// result is 25
|
|
|
|
// ChainFirst executes for side effects but keeps original value
|
|
result := F.Pipe1(
|
|
42,
|
|
identity.ChainFirst(func(n int) string {
|
|
return fmt.Sprintf("Value: %d", n)
|
|
}),
|
|
)
|
|
// result is still 42
|
|
|
|
# Do Notation
|
|
|
|
The package provides "do notation" for imperative-style composition:
|
|
|
|
type Result struct {
|
|
X int
|
|
Y int
|
|
Sum int
|
|
}
|
|
|
|
result := F.Pipe3(
|
|
identity.Do(Result{}),
|
|
identity.Bind(
|
|
func(r Result) func(int) Result {
|
|
return func(x int) Result {
|
|
r.X = x
|
|
return r
|
|
}
|
|
},
|
|
func(Result) int { return 10 },
|
|
),
|
|
identity.Bind(
|
|
func(r Result) func(int) Result {
|
|
return func(y int) Result {
|
|
r.Y = y
|
|
return r
|
|
}
|
|
},
|
|
func(Result) int { return 20 },
|
|
),
|
|
identity.Let(
|
|
func(r Result) func(int) Result {
|
|
return func(sum int) Result {
|
|
r.Sum = sum
|
|
return r
|
|
}
|
|
},
|
|
func(r Result) int { return r.X + r.Y },
|
|
),
|
|
)
|
|
// result is Result{X: 10, Y: 20, Sum: 30}
|
|
|
|
# Sequence and Traverse
|
|
|
|
Convert tuples of Identity values:
|
|
|
|
import T "github.com/IBM/fp-go/v2/tuple"
|
|
|
|
// Sequence a tuple
|
|
tuple := T.MakeTuple2(1, 2)
|
|
result := identity.SequenceTuple2(tuple)
|
|
// result is T.Tuple2[int, int]{1, 2}
|
|
|
|
// Traverse with transformation
|
|
tuple := T.MakeTuple2(1, 2)
|
|
result := identity.TraverseTuple2(
|
|
func(n int) int { return n * 2 },
|
|
func(n int) int { return n * 3 },
|
|
)(tuple)
|
|
// result is T.Tuple2[int, int]{2, 6}
|
|
|
|
# Monad Interface
|
|
|
|
Get a monad instance for generic code:
|
|
|
|
m := identity.Monad[int, string]()
|
|
|
|
// Use monad operations
|
|
value := m.Of(42)
|
|
mapped := m.Map(func(n int) string {
|
|
return fmt.Sprintf("Number: %d", n)
|
|
})(value)
|
|
|
|
# Why Identity?
|
|
|
|
The Identity monad might seem pointless, but it's useful for:
|
|
|
|
1. Testing: Test monad transformers with a simple base monad
|
|
2. Defaults: Provide a default when no specific monad is needed
|
|
3. Learning: Understand monad laws without additional complexity
|
|
4. Abstraction: Write generic code that works with any monad
|
|
|
|
Example of generic code:
|
|
|
|
func ProcessWithMonad[M any](
|
|
monad monad.Monad[int, string, M, M, func(int) M],
|
|
value int,
|
|
) M {
|
|
return F.Pipe2(
|
|
monad.Of(value),
|
|
monad.Map(func(n int) int { return n * 2 }),
|
|
monad.Map(func(n int) string { return fmt.Sprintf("%d", n) }),
|
|
)
|
|
}
|
|
|
|
// Works with Identity
|
|
result := ProcessWithMonad(identity.Monad[int, string](), 21)
|
|
// result is "42"
|
|
|
|
# Type Alias
|
|
|
|
The package defines:
|
|
|
|
type Operator[A, B any] = func(A) B
|
|
|
|
This represents an Identity computation from A to B, which is just a function.
|
|
|
|
# Functions
|
|
|
|
Core operations:
|
|
- Of[A any](A) A - Wrap a value (identity)
|
|
- Map[A, B any](func(A) B) func(A) B - Transform value
|
|
- Chain[A, B any](func(A) B) func(A) B - Monadic bind
|
|
- Ap[B, A any](A) func(func(A) B) B - Apply function
|
|
|
|
Monad variants:
|
|
- MonadMap, MonadChain, MonadAp - Uncurried versions
|
|
|
|
Additional operations:
|
|
- MapTo[A, B any](B) func(A) B - Replace with constant
|
|
- ChainFirst[A, B any](func(A) B) func(A) A - Execute for effect
|
|
- Flap[B, A any](A) func(func(A) B) B - Flip application
|
|
|
|
Do notation:
|
|
- Do[S any](S) S - Initialize context
|
|
- Bind[S1, S2, T any] - Bind computation result
|
|
- Let[S1, S2, T any] - Bind pure value
|
|
- LetTo[S1, S2, B any] - Bind constant
|
|
- BindTo[S1, T any] - Initialize from value
|
|
- ApS[S1, S2, T any] - Apply in context
|
|
|
|
Sequence/Traverse:
|
|
- SequenceT1-10 - Sequence tuples of size 1-10
|
|
- SequenceTuple1-10 - Sequence tuple types
|
|
- TraverseTuple1-10 - Traverse with transformations
|
|
|
|
Monad instance:
|
|
- Monad[A, B any]() - Get monad interface
|
|
|
|
# Related Packages
|
|
|
|
- function: Function composition utilities
|
|
- monad: Monad interface definition
|
|
- tuple: Tuple types for sequence operations
|
|
*/
|
|
package identity
|
|
|
|
//go:generate go run .. identity --count 10 --filename gen.go
|