mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-21 23:47:34 +02:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78ae037d73 | ||
|
|
107618cef8 | ||
|
|
a6ae80845f | ||
|
|
8e7fc699a1 | ||
|
|
9919b75fe6 | ||
|
|
e2da181297 | ||
|
|
24960f87a0 | ||
|
|
6cffae8f89 | ||
|
|
4fa30b60c0 | ||
|
|
6b03e19c67 | ||
|
|
3be4a9ee9d | ||
|
|
b553ff8d9d | ||
|
|
5bb85ecca7 | ||
|
|
2d2f4fa325 | ||
|
|
e7e58c9d54 | ||
|
|
e20679d73c | ||
|
|
b8013af7fa | ||
|
|
4d008e636a | ||
|
|
8a4ecd2203 | ||
|
|
24a7cae6e9 | ||
|
|
c641a1ae7c | ||
|
|
d6766c5439 | ||
|
|
b53ae32570 | ||
|
|
eb4218c575 | ||
|
|
ddb9e48441 |
69
.github/workflows/build.yml
vendored
69
.github/workflows/build.yml
vendored
@@ -4,9 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
dryRun:
|
dryRun:
|
||||||
@@ -15,35 +13,60 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Currently no way to detect automatically
|
|
||||||
DEFAULT_BRANCH: main
|
DEFAULT_BRANCH: main
|
||||||
GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
|
LATEST_GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
|
||||||
NODE_VERSION: 22
|
NODE_VERSION: 22
|
||||||
DRY_RUN: true
|
DRY_RUN: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-v1:
|
||||||
|
name: Build v1 (Go ${{ matrix.go-version }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ '1.20.x', '1.21.x', '1.22.x', '1.23.x']
|
go-version: ['1.20.x', '1.21.x', '1.22.x', '1.23.x']
|
||||||
|
fail-fast: false # Continue with other versions if one fails
|
||||||
steps:
|
steps:
|
||||||
# full checkout for semantic-release
|
# full checkout for semantic-release
|
||||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up go ${{ matrix.go-version }}
|
- name: Set up Go ${{ matrix.go-version }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
-
|
cache: true # Enable Go module caching
|
||||||
name: Tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go test -v ./...
|
go test -v ./...
|
||||||
|
|
||||||
|
build-v2:
|
||||||
|
name: Build v2 (Go ${{ matrix.go-version }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version: ['1.24.x']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up Go ${{ matrix.go-version }}
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go-version }}
|
||||||
|
cache: true # Enable Go module caching
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd v2
|
||||||
|
go mod tidy
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
release:
|
release:
|
||||||
needs: [build]
|
name: Release
|
||||||
|
needs:
|
||||||
|
- build-v1
|
||||||
|
- build-v2
|
||||||
if: github.repository == 'IBM/fp-go' && github.event_name != 'pull_request'
|
if: github.repository == 'IBM/fp-go' && github.event_name != 'pull_request'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
@@ -51,7 +74,6 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# full checkout for semantic-release
|
# full checkout for semantic-release
|
||||||
- name: Full checkout
|
- name: Full checkout
|
||||||
@@ -63,26 +85,27 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
cache: 'npm' # Enable npm caching
|
||||||
|
|
||||||
- name: Set up go ${{env.GO_VERSION}}
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{env.GO_VERSION}}
|
go-version: ${{ env.LATEST_GO_VERSION }}
|
||||||
|
cache: true # Enable Go module caching
|
||||||
|
|
||||||
# The dry-run evaluation is only made for non PR events. Manual trigger w/dryRun true, main branch and any tagged branches will set DRY run to false
|
- name: Determine release mode
|
||||||
- name: Check dry run
|
id: release-mode
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{github.event_name}}" == "workflow_dispatch" && "${{ github.event.inputs.dryRun }}" != "true" ]]; then
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.dryRun }}" != "true" ]]; then
|
||||||
echo "DRY_RUN=false" >> $GITHUB_ENV
|
|
||||||
elif [[ "${{github.ref}}" == "refs/heads/${{env.DEFAULT_BRANCH}}" ]]; then
|
|
||||||
echo "DRY_RUN=false" >> $GITHUB_ENV
|
echo "DRY_RUN=false" >> $GITHUB_ENV
|
||||||
elif [[ "${{github.ref}}" =~ ^refs/heads/v[0-9]+(\.[0-9]+)?$ ]]; then
|
elif [[ "${{ github.ref }}" == "refs/heads/${{ env.DEFAULT_BRANCH }}" ]]; then
|
||||||
|
echo "DRY_RUN=false" >> $GITHUB_ENV
|
||||||
|
elif [[ "${{ github.ref }}" =~ ^refs/heads/v[0-9]+(\.[0-9]+)?$ ]]; then
|
||||||
echo "DRY_RUN=false" >> $GITHUB_ENV
|
echo "DRY_RUN=false" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Semantic Release
|
- name: Run semantic release
|
||||||
run: |
|
run: |
|
||||||
npx -p conventional-changelog-conventionalcommits -p semantic-release semantic-release --dry-run ${{env.DRY_RUN}}
|
npx -p conventional-changelog-conventionalcommits -p semantic-release semantic-release --dry-run ${{ env.DRY_RUN }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package file
|
package bracket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
|||||||
43
v2/README.md
Normal file
43
v2/README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Functional programming library for golang V2
|
||||||
|
|
||||||
|
Go 1.24 introduces [generic type aliases](https://github.com/golang/go/issues/46477) which are leveraged by V2.
|
||||||
|
|
||||||
|
## ⚠️ Breaking Changes
|
||||||
|
|
||||||
|
- use of [generic type aliases](https://github.com/golang/go/issues/46477) which requires [go1.24](https://tip.golang.org/doc/go1.24)
|
||||||
|
- order of generic type arguments adjusted such that types that _cannot_ be inferred by the method argument come first, e.g. in the `Ap` methods
|
||||||
|
- monadic operations for `Pair` operate on the second argument, to be compatible with the [Haskell](https://hackage.haskell.org/package/TypeCompose-0.9.14/docs/Data-Pair.html) definition
|
||||||
|
|
||||||
|
## Simplifications
|
||||||
|
|
||||||
|
- use type aliases to get rid of namespace imports for type declarations, e.g. instead of
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
ET "github.com/IBM/fp-go/v2/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doSth() ET.Either[error, string] {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
you can declare your type once
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Either[A any] = either.Either[error, A]
|
||||||
|
```
|
||||||
|
|
||||||
|
and then use it across your codebase
|
||||||
|
|
||||||
|
```go
|
||||||
|
func doSth() Either[string] {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- library implementation does no longer need to use the `generic` subpackage, this simplifies reading and understanding of the code
|
||||||
46
v2/array/any.go
Normal file
46
v2/array/any.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnyWithIndex tests if any of the elements in the array matches the predicate.
|
||||||
|
// The predicate receives both the index and the element.
|
||||||
|
// Returns true if at least one element satisfies the predicate, false otherwise.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// hasEvenAtEvenIndex := array.AnyWithIndex(func(i, x int) bool {
|
||||||
|
// return i%2 == 0 && x%2 == 0
|
||||||
|
// })
|
||||||
|
// result := hasEvenAtEvenIndex([]int{1, 3, 4, 5}) // true (4 is at index 2)
|
||||||
|
func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool {
|
||||||
|
return G.AnyWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any tests if any of the elements in the array matches the predicate.
|
||||||
|
// Returns true if at least one element satisfies the predicate, false otherwise.
|
||||||
|
// Returns false for an empty array.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// hasEven := array.Any(func(x int) bool { return x%2 == 0 })
|
||||||
|
// result := hasEven([]int{1, 3, 4, 5}) // true
|
||||||
|
func Any[A any](pred func(A) bool) func([]A) bool {
|
||||||
|
return G.Any[[]A](pred)
|
||||||
|
}
|
||||||
30
v2/array/any_test.go
Normal file
30
v2/array/any_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAny(t *testing.T) {
|
||||||
|
anyBool := Any(F.Identity[bool])
|
||||||
|
|
||||||
|
assert.True(t, anyBool(From(false, true, false)))
|
||||||
|
assert.False(t, anyBool(From(false, false, false)))
|
||||||
|
}
|
||||||
439
v2/array/array.go
Normal file
439
v2/array/array.go
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
EM "github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/array"
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// From constructs an array from a set of variadic arguments
|
||||||
|
func From[A any](data ...A) []A {
|
||||||
|
return G.From[[]A](data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`.
|
||||||
|
func MakeBy[F ~func(int) A, A any](n int, f F) []A {
|
||||||
|
return G.MakeBy[[]A](n, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate creates a `Array` containing a value repeated the specified number of times.
|
||||||
|
func Replicate[A any](n int, a A) []A {
|
||||||
|
return G.Replicate[[]A](n, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMap applies a function to each element of an array, returning a new array with the results.
|
||||||
|
// This is the monadic version of Map that takes the array as the first parameter.
|
||||||
|
func MonadMap[A, B any](as []A, f func(a A) B) []B {
|
||||||
|
return G.MonadMap[[]A, []B](as, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMapRef applies a function to a pointer to each element of an array, returning a new array with the results.
|
||||||
|
// This is useful when you need to access elements by reference without copying.
|
||||||
|
func MonadMapRef[A, B any](as []A, f func(a *A) B) []B {
|
||||||
|
count := len(as)
|
||||||
|
bs := make([]B, count)
|
||||||
|
for i := count - 1; i >= 0; i-- {
|
||||||
|
bs[i] = f(&as[i])
|
||||||
|
}
|
||||||
|
return bs
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapWithIndex applies a function to each element and its index in an array, returning a new array with the results.
|
||||||
|
func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
|
||||||
|
return G.MapWithIndex[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map applies a function to each element of an array, returning a new array with the results.
|
||||||
|
// This is the curried version that returns a function.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// double := array.Map(func(x int) int { return x * 2 })
|
||||||
|
// result := double([]int{1, 2, 3}) // [2, 4, 6]
|
||||||
|
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||||
|
return G.Map[[]A, []B, A, B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapRef applies a function to a pointer to each element of an array, returning a new array with the results.
|
||||||
|
// This is the curried version that returns a function.
|
||||||
|
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
||||||
|
return F.Bind2nd(MonadMapRef[A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
||||||
|
var result []A
|
||||||
|
count := len(fa)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
a := fa[i]
|
||||||
|
if pred(&a) {
|
||||||
|
result = append(result, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
|
||||||
|
var result []B
|
||||||
|
count := len(fa)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
a := fa[i]
|
||||||
|
if pred(&a) {
|
||||||
|
result = append(result, f(&a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns a new array with all elements from the original array that match a predicate
|
||||||
|
func Filter[A any](pred func(A) bool) EM.Endomorphism[[]A] {
|
||||||
|
return G.Filter[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterWithIndex returns a new array with all elements from the original array that match a predicate
|
||||||
|
func FilterWithIndex[A any](pred func(int, A) bool) EM.Endomorphism[[]A] {
|
||||||
|
return G.FilterWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterRef returns a new array with all elements from the original array that match a predicate operating on pointers.
|
||||||
|
func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] {
|
||||||
|
return F.Bind2nd(filterRef[A], pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadFilterMap maps an array with a function that returns an Option and keeps only the Some values.
|
||||||
|
// This is the monadic version that takes the array as the first parameter.
|
||||||
|
func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B {
|
||||||
|
return G.MonadFilterMap[[]A, []B](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadFilterMapWithIndex maps an array with a function that takes an index and returns an Option,
|
||||||
|
// keeping only the Some values. This is the monadic version that takes the array as the first parameter.
|
||||||
|
func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) O.Option[B]) []B {
|
||||||
|
return G.MonadFilterMapWithIndex[[]A, []B](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMap maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
||||||
|
func FilterMap[A, B any](f func(A) O.Option[B]) func([]A) []B {
|
||||||
|
return G.FilterMap[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMapWithIndex maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones.
|
||||||
|
func FilterMapWithIndex[A, B any](f func(int, A) O.Option[B]) func([]A) []B {
|
||||||
|
return G.FilterMapWithIndex[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result.
|
||||||
|
func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B {
|
||||||
|
return G.FilterChain[[]A](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMapRef filters an array using a predicate on pointers and maps the matching elements using a function on pointers.
|
||||||
|
func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B {
|
||||||
|
return func(fa []A) []B {
|
||||||
|
return filterMapRef(fa, pred, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B {
|
||||||
|
current := initial
|
||||||
|
count := len(fa)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
current = f(current, &fa[i])
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce folds an array from left to right, applying a function to accumulate a result.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// sum := array.Reduce(func(acc, x int) int { return acc + x }, 0)
|
||||||
|
// result := sum([]int{1, 2, 3, 4, 5}) // 15
|
||||||
|
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
||||||
|
return G.Reduce[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceWithIndex folds an array from left to right with access to the index,
|
||||||
|
// applying a function to accumulate a result.
|
||||||
|
func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceWithIndex[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceRight folds an array from right to left, applying a function to accumulate a result.
|
||||||
|
func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceRight[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceRightWithIndex folds an array from right to left with access to the index,
|
||||||
|
// applying a function to accumulate a result.
|
||||||
|
func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B {
|
||||||
|
return G.ReduceRightWithIndex[[]A](f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceRef folds an array from left to right using pointers to elements,
|
||||||
|
// applying a function to accumulate a result.
|
||||||
|
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
||||||
|
return func(as []A) B {
|
||||||
|
return reduceRef(as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append adds an element to the end of an array, returning a new array.
|
||||||
|
func Append[A any](as []A, a A) []A {
|
||||||
|
return G.Append(as, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks if an array has no elements.
|
||||||
|
func IsEmpty[A any](as []A) bool {
|
||||||
|
return G.IsEmpty(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonEmpty checks if an array has at least one element.
|
||||||
|
func IsNonEmpty[A any](as []A) bool {
|
||||||
|
return len(as) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns an empty array of type A.
|
||||||
|
func Empty[A any]() []A {
|
||||||
|
return G.Empty[[]A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero returns an empty array of type A (alias for Empty).
|
||||||
|
func Zero[A any]() []A {
|
||||||
|
return Empty[A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of constructs a single element array
|
||||||
|
func Of[A any](a A) []A {
|
||||||
|
return G.Of[[]A](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChain applies a function that returns an array to each element and flattens the results.
|
||||||
|
// This is the monadic version that takes the array as the first parameter (also known as FlatMap).
|
||||||
|
func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
||||||
|
return G.MonadChain[[]A, []B](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain applies a function that returns an array to each element and flattens the results.
|
||||||
|
// This is the curried version (also known as FlatMap).
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// duplicate := array.Chain(func(x int) []int { return []int{x, x} })
|
||||||
|
// result := duplicate([]int{1, 2, 3}) // [1, 1, 2, 2, 3, 3]
|
||||||
|
func Chain[A, B any](f func(A) []B) func([]A) []B {
|
||||||
|
return G.Chain[[]A, []B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadAp applies an array of functions to an array of values, producing all combinations.
|
||||||
|
// This is the monadic version that takes both arrays as parameters.
|
||||||
|
func MonadAp[B, A any](fab []func(A) B, fa []A) []B {
|
||||||
|
return G.MonadAp[[]B](fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ap applies an array of functions to an array of values, producing all combinations.
|
||||||
|
// This is the curried version.
|
||||||
|
func Ap[B, A any](fa []A) func([]func(A) B) []B {
|
||||||
|
return G.Ap[[]B, []func(A) B](fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match performs pattern matching on an array, calling onEmpty if empty or onNonEmpty if not.
|
||||||
|
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
||||||
|
return G.Match[[]A](onEmpty, onNonEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchLeft performs pattern matching on an array, calling onEmpty if empty or onNonEmpty with head and tail if not.
|
||||||
|
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
|
||||||
|
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail returns all elements except the first, wrapped in an Option.
|
||||||
|
// Returns None if the array is empty.
|
||||||
|
func Tail[A any](as []A) O.Option[[]A] {
|
||||||
|
return G.Tail(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head returns the first element of an array, wrapped in an Option.
|
||||||
|
// Returns None if the array is empty.
|
||||||
|
func Head[A any](as []A) O.Option[A] {
|
||||||
|
return G.Head(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returns the first element of an array, wrapped in an Option (alias for Head).
|
||||||
|
// Returns None if the array is empty.
|
||||||
|
func First[A any](as []A) O.Option[A] {
|
||||||
|
return G.First(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last returns the last element of an array, wrapped in an Option.
|
||||||
|
// Returns None if the array is empty.
|
||||||
|
func Last[A any](as []A) O.Option[A] {
|
||||||
|
return G.Last(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrependAll inserts a separator before each element of an array.
|
||||||
|
func PrependAll[A any](middle A) EM.Endomorphism[[]A] {
|
||||||
|
return func(as []A) []A {
|
||||||
|
count := len(as)
|
||||||
|
dst := count * 2
|
||||||
|
result := make([]A, dst)
|
||||||
|
for i := count - 1; i >= 0; i-- {
|
||||||
|
dst--
|
||||||
|
result[dst] = as[i]
|
||||||
|
dst--
|
||||||
|
result[dst] = middle
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersperse inserts a separator between each element of an array.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := array.Intersperse(0)([]int{1, 2, 3}) // [1, 0, 2, 0, 3]
|
||||||
|
func Intersperse[A any](middle A) EM.Endomorphism[[]A] {
|
||||||
|
prepend := PrependAll(middle)
|
||||||
|
return func(as []A) []A {
|
||||||
|
if IsEmpty(as) {
|
||||||
|
return as
|
||||||
|
}
|
||||||
|
return prepend(as)[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercalate inserts a separator between elements and concatenates them using a Monoid.
|
||||||
|
func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
|
||||||
|
return func(middle A) func([]A) A {
|
||||||
|
return Match(m.Empty, F.Flow2(Intersperse(middle), ConcatAll[A](m)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten converts a nested array into a flat array by concatenating all inner arrays.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := array.Flatten([][]int{{1, 2}, {3, 4}, {5}}) // [1, 2, 3, 4, 5]
|
||||||
|
func Flatten[A any](mma [][]A) []A {
|
||||||
|
return G.Flatten(mma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice extracts a subarray from index low (inclusive) to high (exclusive).
|
||||||
|
func Slice[A any](low, high int) func(as []A) []A {
|
||||||
|
return array.Slice[[]A](low, high)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup returns the element at the specified index, wrapped in an Option.
|
||||||
|
// Returns None if the index is out of bounds.
|
||||||
|
func Lookup[A any](idx int) func([]A) O.Option[A] {
|
||||||
|
return G.Lookup[[]A](idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpsertAt returns a function that inserts or updates an element at a specific index.
|
||||||
|
// If the index is out of bounds, the element is appended.
|
||||||
|
func UpsertAt[A any](a A) EM.Endomorphism[[]A] {
|
||||||
|
return G.UpsertAt[[]A](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the number of elements in an array.
|
||||||
|
func Size[A any](as []A) int {
|
||||||
|
return G.Size(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadPartition splits an array into two arrays based on a predicate.
|
||||||
|
// The first array contains elements for which the predicate returns false,
|
||||||
|
// the second contains elements for which it returns true.
|
||||||
|
func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] {
|
||||||
|
return G.MonadPartition(as, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partition creates two new arrays out of one, the left result contains the elements
|
||||||
|
// for which the predicate returns false, the right one those for which the predicate returns true
|
||||||
|
func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] {
|
||||||
|
return G.Partition[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil checks if the array is set to nil
|
||||||
|
func IsNil[A any](as []A) bool {
|
||||||
|
return array.IsNil(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonNil checks if the array is set to nil
|
||||||
|
func IsNonNil[A any](as []A) bool {
|
||||||
|
return array.IsNonNil(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstNil returns a nil array
|
||||||
|
func ConstNil[A any]() []A {
|
||||||
|
return array.ConstNil[[]A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceRight extracts a subarray from the specified start index to the end.
|
||||||
|
func SliceRight[A any](start int) EM.Endomorphism[[]A] {
|
||||||
|
return G.SliceRight[[]A](start)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a shallow copy of the array
|
||||||
|
func Copy[A any](b []A) []A {
|
||||||
|
return G.Copy(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a deep copy of the array using the provided endomorphism to clone the values
|
||||||
|
func Clone[A any](f func(A) A) func(as []A) []A {
|
||||||
|
return G.Clone[[]A](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
|
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||||
|
return G.FoldMap[[]A](m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FoldMapWithIndex maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
|
func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func([]A) B {
|
||||||
|
return G.FoldMapWithIndex[[]A](m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold folds the array using the provided Monoid.
|
||||||
|
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||||
|
return G.Fold[[]A](m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push adds an element to the end of an array (alias for Append).
|
||||||
|
func Push[A any](a A) EM.Endomorphism[[]A] {
|
||||||
|
return G.Push[EM.Endomorphism[[]A]](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadFlap applies a value to an array of functions, producing an array of results.
|
||||||
|
// This is the monadic version that takes both parameters.
|
||||||
|
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||||
|
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flap applies a value to an array of functions, producing an array of results.
|
||||||
|
// This is the curried version.
|
||||||
|
func Flap[B, A any](a A) func([]func(A) B) []B {
|
||||||
|
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend adds an element to the beginning of an array, returning a new array.
|
||||||
|
func Prepend[A any](head A) EM.Endomorphism[[]A] {
|
||||||
|
return G.Prepend[EM.Endomorphism[[]A]](head)
|
||||||
|
}
|
||||||
323
v2/array/array_extended_test.go
Normal file
323
v2/array/array_extended_test.go
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
N "github.com/IBM/fp-go/v2/number"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
S "github.com/IBM/fp-go/v2/string"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReplicate(t *testing.T) {
|
||||||
|
result := Replicate(3, "a")
|
||||||
|
assert.Equal(t, []string{"a", "a", "a"}, result)
|
||||||
|
|
||||||
|
empty := Replicate(0, 42)
|
||||||
|
assert.Equal(t, []int{}, empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMap(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
result := MonadMap(src, func(x int) int { return x * 2 })
|
||||||
|
assert.Equal(t, []int{2, 4, 6}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMapRef(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
result := MonadMapRef(src, func(x *int) int { return *x * 2 })
|
||||||
|
assert.Equal(t, []int{2, 4, 6}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapWithIndex(t *testing.T) {
|
||||||
|
src := []string{"a", "b", "c"}
|
||||||
|
mapper := MapWithIndex(func(i int, s string) string {
|
||||||
|
return fmt.Sprintf("%d:%s", i, s)
|
||||||
|
})
|
||||||
|
result := mapper(src)
|
||||||
|
assert.Equal(t, []string{"0:a", "1:b", "2:c"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapRef(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
mapper := MapRef(func(x *int) int { return *x * 2 })
|
||||||
|
result := mapper(src)
|
||||||
|
assert.Equal(t, []int{2, 4, 6}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
filter := FilterWithIndex(func(i, x int) bool {
|
||||||
|
return i%2 == 0 && x > 2
|
||||||
|
})
|
||||||
|
result := filter(src)
|
||||||
|
assert.Equal(t, []int{3, 5}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterRef(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
filter := FilterRef(func(x *int) bool { return *x > 2 })
|
||||||
|
result := filter(src)
|
||||||
|
assert.Equal(t, []int{3, 4, 5}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadFilterMap(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4}
|
||||||
|
result := MonadFilterMap(src, func(x int) O.Option[string] {
|
||||||
|
if x%2 == 0 {
|
||||||
|
return O.Some(fmt.Sprintf("even:%d", x))
|
||||||
|
}
|
||||||
|
return O.None[string]()
|
||||||
|
})
|
||||||
|
assert.Equal(t, []string{"even:2", "even:4"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadFilterMapWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4}
|
||||||
|
result := MonadFilterMapWithIndex(src, func(i, x int) O.Option[string] {
|
||||||
|
if i%2 == 0 {
|
||||||
|
return O.Some(fmt.Sprintf("%d:%d", i, x))
|
||||||
|
}
|
||||||
|
return O.None[string]()
|
||||||
|
})
|
||||||
|
assert.Equal(t, []string{"0:1", "2:3"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterMapWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4}
|
||||||
|
filter := FilterMapWithIndex(func(i, x int) O.Option[string] {
|
||||||
|
if i%2 == 0 {
|
||||||
|
return O.Some(fmt.Sprintf("%d:%d", i, x))
|
||||||
|
}
|
||||||
|
return O.None[string]()
|
||||||
|
})
|
||||||
|
result := filter(src)
|
||||||
|
assert.Equal(t, []string{"0:1", "2:3"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterMapRef(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
filter := FilterMapRef(
|
||||||
|
func(x *int) bool { return *x > 2 },
|
||||||
|
func(x *int) string { return fmt.Sprintf("val:%d", *x) },
|
||||||
|
)
|
||||||
|
result := filter(src)
|
||||||
|
assert.Equal(t, []string{"val:3", "val:4", "val:5"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduceWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
reducer := ReduceWithIndex(func(i, acc, x int) int {
|
||||||
|
return acc + i + x
|
||||||
|
}, 0)
|
||||||
|
result := reducer(src)
|
||||||
|
assert.Equal(t, 9, result) // 0 + (0+1) + (1+2) + (2+3) = 9
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduceRightWithIndex(t *testing.T) {
|
||||||
|
src := []string{"a", "b", "c"}
|
||||||
|
reducer := ReduceRightWithIndex(func(i int, x, acc string) string {
|
||||||
|
return fmt.Sprintf("%s%d:%s", acc, i, x)
|
||||||
|
}, "")
|
||||||
|
result := reducer(src)
|
||||||
|
assert.Equal(t, "2:c1:b0:a", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduceRef(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
reducer := ReduceRef(func(acc int, x *int) int {
|
||||||
|
return acc + *x
|
||||||
|
}, 0)
|
||||||
|
result := reducer(src)
|
||||||
|
assert.Equal(t, 6, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZero(t *testing.T) {
|
||||||
|
result := Zero[int]()
|
||||||
|
assert.Equal(t, []int{}, result)
|
||||||
|
assert.True(t, IsEmpty(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChain(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
result := MonadChain(src, func(x int) []int {
|
||||||
|
return []int{x, x * 10}
|
||||||
|
})
|
||||||
|
assert.Equal(t, []int{1, 10, 2, 20, 3, 30}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChain(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
chain := Chain(func(x int) []int {
|
||||||
|
return []int{x, x * 10}
|
||||||
|
})
|
||||||
|
result := chain(src)
|
||||||
|
assert.Equal(t, []int{1, 10, 2, 20, 3, 30}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadAp(t *testing.T) {
|
||||||
|
fns := []func(int) int{
|
||||||
|
func(x int) int { return x * 2 },
|
||||||
|
func(x int) int { return x + 10 },
|
||||||
|
}
|
||||||
|
values := []int{1, 2}
|
||||||
|
result := MonadAp(fns, values)
|
||||||
|
assert.Equal(t, []int{2, 4, 11, 12}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchLeft(t *testing.T) {
|
||||||
|
matcher := MatchLeft(
|
||||||
|
func() string { return "empty" },
|
||||||
|
func(head int, tail []int) string {
|
||||||
|
return fmt.Sprintf("head:%d,tail:%v", head, tail)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, "empty", matcher([]int{}))
|
||||||
|
assert.Equal(t, "head:1,tail:[2 3]", matcher([]int{1, 2, 3}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTail(t *testing.T) {
|
||||||
|
assert.Equal(t, O.None[[]int](), Tail([]int{}))
|
||||||
|
assert.Equal(t, O.Some([]int{2, 3}), Tail([]int{1, 2, 3}))
|
||||||
|
assert.Equal(t, O.Some([]int{}), Tail([]int{1}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFirst(t *testing.T) {
|
||||||
|
assert.Equal(t, O.None[int](), First([]int{}))
|
||||||
|
assert.Equal(t, O.Some(1), First([]int{1, 2, 3}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLast(t *testing.T) {
|
||||||
|
assert.Equal(t, O.None[int](), Last([]int{}))
|
||||||
|
assert.Equal(t, O.Some(3), Last([]int{1, 2, 3}))
|
||||||
|
assert.Equal(t, O.Some(1), Last([]int{1}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpsertAt(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
upsert := UpsertAt(99)
|
||||||
|
|
||||||
|
result1 := upsert(src)
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 99}, result1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSize(t *testing.T) {
|
||||||
|
assert.Equal(t, 0, Size([]int{}))
|
||||||
|
assert.Equal(t, 3, Size([]int{1, 2, 3}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadPartition(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
result := MonadPartition(src, func(x int) bool { return x > 2 })
|
||||||
|
assert.Equal(t, []int{1, 2}, result.F1)
|
||||||
|
assert.Equal(t, []int{3, 4, 5}, result.F2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNil(t *testing.T) {
|
||||||
|
var nilSlice []int
|
||||||
|
assert.True(t, IsNil(nilSlice))
|
||||||
|
assert.False(t, IsNil([]int{}))
|
||||||
|
assert.False(t, IsNil([]int{1}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNonNil(t *testing.T) {
|
||||||
|
var nilSlice []int
|
||||||
|
assert.False(t, IsNonNil(nilSlice))
|
||||||
|
assert.True(t, IsNonNil([]int{}))
|
||||||
|
assert.True(t, IsNonNil([]int{1}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConstNil(t *testing.T) {
|
||||||
|
result := ConstNil[int]()
|
||||||
|
assert.True(t, IsNil(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceRight(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
slicer := SliceRight[int](2)
|
||||||
|
result := slicer(src)
|
||||||
|
assert.Equal(t, []int{3, 4, 5}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
copied := Copy(src)
|
||||||
|
assert.Equal(t, src, copied)
|
||||||
|
// Verify it's a different slice
|
||||||
|
copied[0] = 99
|
||||||
|
assert.Equal(t, 1, src[0])
|
||||||
|
assert.Equal(t, 99, copied[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClone(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
cloner := Clone(func(x int) int { return x * 2 })
|
||||||
|
result := cloner(src)
|
||||||
|
assert.Equal(t, []int{2, 4, 6}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFoldMapWithIndex(t *testing.T) {
|
||||||
|
src := []string{"a", "b", "c"}
|
||||||
|
folder := FoldMapWithIndex[string](S.Monoid)(func(i int, s string) string {
|
||||||
|
return fmt.Sprintf("%d:%s", i, s)
|
||||||
|
})
|
||||||
|
result := folder(src)
|
||||||
|
assert.Equal(t, "0:a1:b2:c", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFold(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
folder := Fold(N.MonoidSum[int]())
|
||||||
|
result := folder(src)
|
||||||
|
assert.Equal(t, 15, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPush(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3}
|
||||||
|
pusher := Push(4)
|
||||||
|
result := pusher(src)
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadFlap(t *testing.T) {
|
||||||
|
fns := []func(int) string{
|
||||||
|
func(x int) string { return fmt.Sprintf("a%d", x) },
|
||||||
|
func(x int) string { return fmt.Sprintf("b%d", x) },
|
||||||
|
}
|
||||||
|
result := MonadFlap(fns, 5)
|
||||||
|
assert.Equal(t, []string{"a5", "b5"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlap(t *testing.T) {
|
||||||
|
fns := []func(int) string{
|
||||||
|
func(x int) string { return fmt.Sprintf("a%d", x) },
|
||||||
|
func(x int) string { return fmt.Sprintf("b%d", x) },
|
||||||
|
}
|
||||||
|
flapper := Flap[string](5)
|
||||||
|
result := flapper(fns)
|
||||||
|
assert.Equal(t, []string{"a5", "b5"}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepend(t *testing.T) {
|
||||||
|
src := []int{2, 3, 4}
|
||||||
|
prepender := Prepend(1)
|
||||||
|
result := prepender(src)
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4}, result)
|
||||||
|
}
|
||||||
216
v2/array/array_test.go
Normal file
216
v2/array/array_test.go
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
S "github.com/IBM/fp-go/v2/string"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMap1(t *testing.T) {
|
||||||
|
|
||||||
|
src := []string{"a", "b", "c"}
|
||||||
|
|
||||||
|
up := Map(strings.ToUpper)(src)
|
||||||
|
|
||||||
|
var up1 = []string{}
|
||||||
|
for _, s := range src {
|
||||||
|
up1 = append(up1, strings.ToUpper(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
var up2 = []string{}
|
||||||
|
for i := range src {
|
||||||
|
up2 = append(up2, strings.ToUpper(src[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, up, up1)
|
||||||
|
assert.Equal(t, up, up2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
|
||||||
|
mapper := Map(utils.Upper)
|
||||||
|
|
||||||
|
src := []string{"a", "b", "c"}
|
||||||
|
|
||||||
|
dst := mapper(src)
|
||||||
|
|
||||||
|
assert.Equal(t, dst, []string{"A", "B", "C"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduceRight(t *testing.T) {
|
||||||
|
values := From("a", "b", "c")
|
||||||
|
f := func(a, acc string) string {
|
||||||
|
return fmt.Sprintf("%s%s", acc, a)
|
||||||
|
}
|
||||||
|
b := ""
|
||||||
|
|
||||||
|
assert.Equal(t, "cba", ReduceRight(f, b)(values))
|
||||||
|
assert.Equal(t, "", ReduceRight(f, b)(Empty[string]()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduce(t *testing.T) {
|
||||||
|
|
||||||
|
values := MakeBy(101, F.Identity[int])
|
||||||
|
|
||||||
|
sum := func(val int, current int) int {
|
||||||
|
return val + current
|
||||||
|
}
|
||||||
|
reducer := Reduce(sum, 0)
|
||||||
|
|
||||||
|
result := reducer(values)
|
||||||
|
assert.Equal(t, result, 5050)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmpty(t *testing.T) {
|
||||||
|
assert.True(t, IsNonEmpty(MakeBy(101, F.Identity[int])))
|
||||||
|
assert.True(t, IsEmpty([]int{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAp(t *testing.T) {
|
||||||
|
assert.Equal(t,
|
||||||
|
[]int{2, 4, 6, 3, 6, 9},
|
||||||
|
F.Pipe1(
|
||||||
|
[]func(int) int{
|
||||||
|
utils.Double,
|
||||||
|
utils.Triple,
|
||||||
|
},
|
||||||
|
Ap[int, int]([]int{1, 2, 3}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntercalate(t *testing.T) {
|
||||||
|
is := Intercalate(S.Monoid)("-")
|
||||||
|
|
||||||
|
assert.Equal(t, "", is(Empty[string]()))
|
||||||
|
assert.Equal(t, "a", is([]string{"a"}))
|
||||||
|
assert.Equal(t, "a-b-c", is([]string{"a", "b", "c"}))
|
||||||
|
assert.Equal(t, "a--c", is([]string{"a", "", "c"}))
|
||||||
|
assert.Equal(t, "a-b", is([]string{"a", "b"}))
|
||||||
|
assert.Equal(t, "a-b-c-d", is([]string{"a", "b", "c", "d"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntersperse(t *testing.T) {
|
||||||
|
// Test with empty array
|
||||||
|
assert.Equal(t, []int{}, Intersperse(0)([]int{}))
|
||||||
|
|
||||||
|
// Test with single element
|
||||||
|
assert.Equal(t, []int{1}, Intersperse(0)([]int{1}))
|
||||||
|
|
||||||
|
// Test with multiple elements
|
||||||
|
assert.Equal(t, []int{1, 0, 2, 0, 3}, Intersperse(0)([]int{1, 2, 3}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrependAll(t *testing.T) {
|
||||||
|
empty := Empty[int]()
|
||||||
|
prep := PrependAll(0)
|
||||||
|
assert.Equal(t, empty, prep(empty))
|
||||||
|
assert.Equal(t, []int{0, 1, 0, 2, 0, 3}, prep([]int{1, 2, 3}))
|
||||||
|
assert.Equal(t, []int{0, 1}, prep([]int{1}))
|
||||||
|
assert.Equal(t, []int{0, 1, 0, 2, 0, 3, 0, 4}, prep([]int{1, 2, 3, 4}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatten(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, Flatten([][]int{{1}, {2}, {3}}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLookup(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2}
|
||||||
|
none := O.None[int]()
|
||||||
|
|
||||||
|
assert.Equal(t, none, Lookup[int](-1)(data))
|
||||||
|
assert.Equal(t, none, Lookup[int](10)(data))
|
||||||
|
assert.Equal(t, O.Some(1), Lookup[int](1)(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlice(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3}
|
||||||
|
|
||||||
|
assert.Equal(t, []int{1, 2}, Slice[int](1, 3)(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrom(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, From(1, 2, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartition(t *testing.T) {
|
||||||
|
|
||||||
|
pred := func(n int) bool {
|
||||||
|
return n > 2
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, T.MakeTuple2(Empty[int](), Empty[int]()), Partition(pred)(Empty[int]()))
|
||||||
|
assert.Equal(t, T.MakeTuple2(From(1), From(3)), Partition(pred)(From(1, 3)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterChain(t *testing.T) {
|
||||||
|
src := From(1, 2, 3)
|
||||||
|
|
||||||
|
f := func(i int) O.Option[[]string] {
|
||||||
|
if i%2 != 0 {
|
||||||
|
return O.Of(From(fmt.Sprintf("a%d", i), fmt.Sprintf("b%d", i)))
|
||||||
|
}
|
||||||
|
return O.None[[]string]()
|
||||||
|
}
|
||||||
|
|
||||||
|
res := FilterChain(f)(src)
|
||||||
|
|
||||||
|
assert.Equal(t, From("a1", "b1", "a3", "b3"), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterMap(t *testing.T) {
|
||||||
|
src := From(1, 2, 3)
|
||||||
|
|
||||||
|
f := func(i int) O.Option[string] {
|
||||||
|
if i%2 != 0 {
|
||||||
|
return O.Of(fmt.Sprintf("a%d", i))
|
||||||
|
}
|
||||||
|
return O.None[string]()
|
||||||
|
}
|
||||||
|
|
||||||
|
res := FilterMap(f)(src)
|
||||||
|
|
||||||
|
assert.Equal(t, From("a1", "a3"), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFoldMap(t *testing.T) {
|
||||||
|
src := From("a", "b", "c")
|
||||||
|
|
||||||
|
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||||
|
|
||||||
|
assert.Equal(t, "ABC", fold(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFoldMap() {
|
||||||
|
src := From("a", "b", "c")
|
||||||
|
|
||||||
|
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||||
|
|
||||||
|
fmt.Println(fold(src))
|
||||||
|
|
||||||
|
// Output: ABC
|
||||||
|
|
||||||
|
}
|
||||||
136
v2/array/bind.go
Normal file
136
v2/array/bind.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do creates an empty context of type S to be used with the Bind operation.
|
||||||
|
// This is the starting point for monadic do-notation style computations.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// X int
|
||||||
|
// Y int
|
||||||
|
// }
|
||||||
|
// result := array.Do(State{})
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) []S {
|
||||||
|
return G.Do[[]S, S](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context S1 to produce a context S2.
|
||||||
|
// The setter function defines how to update the context with the computation result.
|
||||||
|
// This enables monadic composition where each step can produce multiple results.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// array.Do(struct{ X, Y int }{}),
|
||||||
|
// array.Bind(
|
||||||
|
// func(x int) func(s struct{}) struct{ X int } {
|
||||||
|
// return func(s struct{}) struct{ X int } { return struct{ X int }{x} }
|
||||||
|
// },
|
||||||
|
// func(s struct{}) []int { return []int{1, 2} },
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
func Bind[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) []T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.Bind[[]S1, []S2, []T, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a pure computation to a context S1 to produce a context S2.
|
||||||
|
// Unlike Bind, the computation function returns a plain value T rather than []T.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := array.Let(
|
||||||
|
// func(sum int) func(s struct{ X int }) struct{ X, Sum int } {
|
||||||
|
// return func(s struct{ X int }) struct{ X, Sum int } {
|
||||||
|
// return struct{ X, Sum int }{s.X, sum}
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// func(s struct{ X int }) int { return s.X * 2 },
|
||||||
|
// )
|
||||||
|
func Let[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.Let[[]S1, []S2, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches a constant value to a context S1 to produce a context S2.
|
||||||
|
// This is useful for adding constant values to the context.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := array.LetTo(
|
||||||
|
// func(name string) func(s struct{ X int }) struct{ X int; Name string } {
|
||||||
|
// return func(s struct{ X int }) struct{ X int; Name string } {
|
||||||
|
// return struct{ X int; Name string }{s.X, name}
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// "constant",
|
||||||
|
// )
|
||||||
|
func LetTo[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.LetTo[[]S1, []S2, S1, S2, T](setter, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state S1 from a value T.
|
||||||
|
// This is typically the first operation after Do to start building the context.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// []int{1, 2, 3},
|
||||||
|
// array.BindTo(func(x int) struct{ X int } {
|
||||||
|
// return struct{ X int }{x}
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
func BindTo[S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func([]T) []S1 {
|
||||||
|
return G.BindTo[[]S1, []T, S1, T](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context S1 to produce a context S2 by considering
|
||||||
|
// the context and the value concurrently (using applicative semantics).
|
||||||
|
// This produces all combinations of context values and array values.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := array.ApS(
|
||||||
|
// func(y int) func(s struct{ X int }) struct{ X, Y int } {
|
||||||
|
// return func(s struct{ X int }) struct{ X, Y int } {
|
||||||
|
// return struct{ X, Y int }{s.X, y}
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// []int{10, 20},
|
||||||
|
// )
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa []T,
|
||||||
|
) func([]S1) []S2 {
|
||||||
|
return G.ApS[[]S1, []S2, []T, S1, S2, T](setter, fa)
|
||||||
|
}
|
||||||
78
v2/array/bind_extended_test.go
Normal file
78
v2/array/bind_extended_test.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestState1 struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestState2 struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLet(t *testing.T) {
|
||||||
|
result := F.Pipe2(
|
||||||
|
Do(TestState1{}),
|
||||||
|
Let(
|
||||||
|
func(y int) func(s TestState1) TestState2 {
|
||||||
|
return func(s TestState1) TestState2 {
|
||||||
|
return TestState2{X: s.X, Y: y}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
func(s TestState1) int { return s.X * 2 },
|
||||||
|
),
|
||||||
|
Map(func(s TestState2) int { return s.X + s.Y }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, []int{0}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLetTo(t *testing.T) {
|
||||||
|
result := F.Pipe2(
|
||||||
|
Do(TestState1{X: 5}),
|
||||||
|
LetTo(
|
||||||
|
func(y int) func(s TestState1) TestState2 {
|
||||||
|
return func(s TestState1) TestState2 {
|
||||||
|
return TestState2{X: s.X, Y: y}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
42,
|
||||||
|
),
|
||||||
|
Map(func(s TestState2) int { return s.X + s.Y }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, []int{47}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindTo(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
[]int{1, 2, 3},
|
||||||
|
BindTo(func(x int) TestState1 {
|
||||||
|
return TestState1{X: x}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := []TestState1{{X: 1}, {X: 2}, {X: 3}}
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
}
|
||||||
56
v2/array/bind_test.go
Normal file
56
v2/array/bind_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) []string {
|
||||||
|
return Of("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) []string {
|
||||||
|
return Of("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res, Of("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res, Of("John Doe"))
|
||||||
|
}
|
||||||
251
v2/array/doc.go
Normal file
251
v2/array/doc.go
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
// 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 array provides functional programming utilities for working with Go slices.
|
||||||
|
//
|
||||||
|
// This package treats Go slices as immutable arrays and provides a rich set of operations
|
||||||
|
// for transforming, filtering, folding, and combining arrays in a functional style.
|
||||||
|
// All operations return new arrays rather than modifying existing ones.
|
||||||
|
//
|
||||||
|
// # Core Concepts
|
||||||
|
//
|
||||||
|
// The array package implements several functional programming abstractions:
|
||||||
|
// - Functor: Transform array elements with Map
|
||||||
|
// - Applicative: Apply functions in arrays to values in arrays
|
||||||
|
// - Monad: Chain operations that produce arrays with Chain/FlatMap
|
||||||
|
// - Foldable: Reduce arrays to single values with Reduce/Fold
|
||||||
|
// - Traversable: Transform arrays while preserving structure
|
||||||
|
//
|
||||||
|
// # Basic Operations
|
||||||
|
//
|
||||||
|
// // Creating arrays
|
||||||
|
// arr := array.From(1, 2, 3, 4, 5)
|
||||||
|
// repeated := array.Replicate(3, "hello")
|
||||||
|
// generated := array.MakeBy(5, func(i int) int { return i * 2 })
|
||||||
|
//
|
||||||
|
// // Transforming arrays
|
||||||
|
// doubled := array.Map(func(x int) int { return x * 2 })(arr)
|
||||||
|
// filtered := array.Filter(func(x int) bool { return x > 2 })(arr)
|
||||||
|
//
|
||||||
|
// // Combining arrays
|
||||||
|
// combined := array.Flatten([][]int{{1, 2}, {3, 4}})
|
||||||
|
// zipped := array.Zip([]string{"a", "b"})([]int{1, 2})
|
||||||
|
//
|
||||||
|
// # Mapping and Filtering
|
||||||
|
//
|
||||||
|
// Transform array elements with Map, or filter elements with Filter:
|
||||||
|
//
|
||||||
|
// numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
//
|
||||||
|
// // Map transforms each element
|
||||||
|
// doubled := array.Map(func(x int) int { return x * 2 })(numbers)
|
||||||
|
// // Result: [2, 4, 6, 8, 10]
|
||||||
|
//
|
||||||
|
// // Filter keeps elements matching a predicate
|
||||||
|
// evens := array.Filter(func(x int) bool { return x%2 == 0 })(numbers)
|
||||||
|
// // Result: [2, 4]
|
||||||
|
//
|
||||||
|
// // FilterMap combines both operations
|
||||||
|
// import "github.com/IBM/fp-go/v2/option"
|
||||||
|
// result := array.FilterMap(func(x int) option.Option[int] {
|
||||||
|
// if x%2 == 0 {
|
||||||
|
// return option.Some(x * 2)
|
||||||
|
// }
|
||||||
|
// return option.None[int]()
|
||||||
|
// })(numbers)
|
||||||
|
// // Result: [4, 8]
|
||||||
|
//
|
||||||
|
// # Folding and Reducing
|
||||||
|
//
|
||||||
|
// Reduce arrays to single values:
|
||||||
|
//
|
||||||
|
// numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
//
|
||||||
|
// // Sum all elements
|
||||||
|
// sum := array.Reduce(func(acc, x int) int { return acc + x }, 0)(numbers)
|
||||||
|
// // Result: 15
|
||||||
|
//
|
||||||
|
// // Using a Monoid
|
||||||
|
// import "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
// sum := array.Fold(monoid.MonoidSum[int]())(numbers)
|
||||||
|
// // Result: 15
|
||||||
|
//
|
||||||
|
// # Chaining Operations
|
||||||
|
//
|
||||||
|
// Chain operations that produce arrays (also known as FlatMap):
|
||||||
|
//
|
||||||
|
// numbers := []int{1, 2, 3}
|
||||||
|
// result := array.Chain(func(x int) []int {
|
||||||
|
// return []int{x, x * 10}
|
||||||
|
// })(numbers)
|
||||||
|
// // Result: [1, 10, 2, 20, 3, 30]
|
||||||
|
//
|
||||||
|
// # Finding Elements
|
||||||
|
//
|
||||||
|
// Search for elements matching predicates:
|
||||||
|
//
|
||||||
|
// numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
//
|
||||||
|
// // Find first element > 3
|
||||||
|
// first := array.FindFirst(func(x int) bool { return x > 3 })(numbers)
|
||||||
|
// // Result: Some(4)
|
||||||
|
//
|
||||||
|
// // Find last element > 3
|
||||||
|
// last := array.FindLast(func(x int) bool { return x > 3 })(numbers)
|
||||||
|
// // Result: Some(5)
|
||||||
|
//
|
||||||
|
// // Get head and tail
|
||||||
|
// head := array.Head(numbers) // Some(1)
|
||||||
|
// tail := array.Tail(numbers) // Some([2, 3, 4, 5])
|
||||||
|
//
|
||||||
|
// # Sorting
|
||||||
|
//
|
||||||
|
// Sort arrays using Ord instances:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/ord"
|
||||||
|
//
|
||||||
|
// numbers := []int{3, 1, 4, 1, 5}
|
||||||
|
// sorted := array.Sort(ord.FromStrictCompare[int]())(numbers)
|
||||||
|
// // Result: [1, 1, 3, 4, 5]
|
||||||
|
//
|
||||||
|
// // Sort by extracted key
|
||||||
|
// type Person struct { Name string; Age int }
|
||||||
|
// people := []Person{{"Alice", 30}, {"Bob", 25}}
|
||||||
|
// byAge := array.SortByKey(ord.FromStrictCompare[int](), func(p Person) int {
|
||||||
|
// return p.Age
|
||||||
|
// })(people)
|
||||||
|
//
|
||||||
|
// # Uniqueness
|
||||||
|
//
|
||||||
|
// Remove duplicate elements:
|
||||||
|
//
|
||||||
|
// numbers := []int{1, 2, 2, 3, 3, 3}
|
||||||
|
// unique := array.StrictUniq(numbers)
|
||||||
|
// // Result: [1, 2, 3]
|
||||||
|
//
|
||||||
|
// // Unique by key
|
||||||
|
// type Person struct { Name string; Age int }
|
||||||
|
// people := []Person{{"Alice", 30}, {"Bob", 25}, {"Alice", 35}}
|
||||||
|
// uniqueByName := array.Uniq(func(p Person) string { return p.Name })(people)
|
||||||
|
// // Result: [{"Alice", 30}, {"Bob", 25}]
|
||||||
|
//
|
||||||
|
// # Zipping
|
||||||
|
//
|
||||||
|
// Combine multiple arrays:
|
||||||
|
//
|
||||||
|
// names := []string{"Alice", "Bob", "Charlie"}
|
||||||
|
// ages := []int{30, 25, 35}
|
||||||
|
//
|
||||||
|
// // Zip into tuples
|
||||||
|
// pairs := array.Zip(ages)(names)
|
||||||
|
// // Result: [(Alice, 30), (Bob, 25), (Charlie, 35)]
|
||||||
|
//
|
||||||
|
// // Zip with custom function
|
||||||
|
// result := array.ZipWith(names, ages, func(name string, age int) string {
|
||||||
|
// return fmt.Sprintf("%s is %d", name, age)
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// # Monadic Do Notation
|
||||||
|
//
|
||||||
|
// Build complex array computations using do-notation style:
|
||||||
|
//
|
||||||
|
// result := array.Do(
|
||||||
|
// struct{ X, Y int }{},
|
||||||
|
// )(
|
||||||
|
// array.Bind(
|
||||||
|
// func(x int) func(s struct{}) struct{ X int } {
|
||||||
|
// return func(s struct{}) struct{ X int } { return struct{ X int }{x} }
|
||||||
|
// },
|
||||||
|
// func(s struct{}) []int { return []int{1, 2, 3} },
|
||||||
|
// ),
|
||||||
|
// array.Bind(
|
||||||
|
// func(y int) func(s struct{ X int }) struct{ X, Y int } {
|
||||||
|
// return func(s struct{ X int }) struct{ X, Y int } {
|
||||||
|
// return struct{ X, Y int }{s.X, y}
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// func(s struct{ X int }) []int { return []int{4, 5} },
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// // Produces all combinations: [{1,4}, {1,5}, {2,4}, {2,5}, {3,4}, {3,5}]
|
||||||
|
//
|
||||||
|
// # Sequence and Traverse
|
||||||
|
//
|
||||||
|
// Transform arrays of effects into effects of arrays:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/option"
|
||||||
|
//
|
||||||
|
// // Sequence: []Option[A] -> Option[[]A]
|
||||||
|
// opts := []option.Option[int]{
|
||||||
|
// option.Some(1),
|
||||||
|
// option.Some(2),
|
||||||
|
// option.Some(3),
|
||||||
|
// }
|
||||||
|
// result := array.ArrayOption[int]()(opts)
|
||||||
|
// // Result: Some([1, 2, 3])
|
||||||
|
//
|
||||||
|
// // If any is None, result is None
|
||||||
|
// opts2 := []option.Option[int]{
|
||||||
|
// option.Some(1),
|
||||||
|
// option.None[int](),
|
||||||
|
// option.Some(3),
|
||||||
|
// }
|
||||||
|
// result2 := array.ArrayOption[int]()(opts2)
|
||||||
|
// // Result: None
|
||||||
|
//
|
||||||
|
// # Equality and Comparison
|
||||||
|
//
|
||||||
|
// Compare arrays for equality:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/eq"
|
||||||
|
//
|
||||||
|
// eq := array.Eq(eq.FromStrictEquals[int]())
|
||||||
|
// equal := eq.Equals([]int{1, 2, 3}, []int{1, 2, 3})
|
||||||
|
// // Result: true
|
||||||
|
//
|
||||||
|
// # Monoid Operations
|
||||||
|
//
|
||||||
|
// Combine arrays using monoid operations:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
//
|
||||||
|
// // Concatenate arrays
|
||||||
|
// m := array.Monoid[int]()
|
||||||
|
// result := m.Concat([]int{1, 2}, []int{3, 4})
|
||||||
|
// // Result: [1, 2, 3, 4]
|
||||||
|
//
|
||||||
|
// // Concatenate multiple arrays efficiently
|
||||||
|
// result := array.ArrayConcatAll(
|
||||||
|
// []int{1, 2},
|
||||||
|
// []int{3, 4},
|
||||||
|
// []int{5, 6},
|
||||||
|
// )
|
||||||
|
// // Result: [1, 2, 3, 4, 5, 6]
|
||||||
|
//
|
||||||
|
// # Performance Considerations
|
||||||
|
//
|
||||||
|
// Most operations create new arrays rather than modifying existing ones. For performance-critical
|
||||||
|
// code, consider:
|
||||||
|
// - Using Copy for shallow copies when needed
|
||||||
|
// - Using Clone with a custom cloning function for deep copies
|
||||||
|
// - Batching operations to minimize intermediate allocations
|
||||||
|
// - Using ArrayConcatAll for efficient concatenation of multiple arrays
|
||||||
|
//
|
||||||
|
// # Subpackages
|
||||||
|
//
|
||||||
|
// - array/generic: Generic implementations for custom array-like types
|
||||||
|
// - array/nonempty: Operations for non-empty arrays with compile-time guarantees
|
||||||
|
// - array/testing: Testing utilities for array laws and properties
|
||||||
|
package array
|
||||||
51
v2/array/eq.go
Normal file
51
v2/array/eq.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
E "github.com/IBM/fp-go/v2/eq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func equals[T any](left []T, right []T, eq func(T, T) bool) bool {
|
||||||
|
if len(left) != len(right) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v1 := range left {
|
||||||
|
v2 := right[i]
|
||||||
|
if !eq(v1, v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eq creates an equality checker for arrays given an equality checker for elements.
|
||||||
|
// Two arrays are considered equal if they have the same length and all corresponding
|
||||||
|
// elements are equal according to the provided Eq instance.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/eq"
|
||||||
|
//
|
||||||
|
// intArrayEq := array.Eq(eq.FromStrictEquals[int]())
|
||||||
|
// result := intArrayEq.Equals([]int{1, 2, 3}, []int{1, 2, 3}) // true
|
||||||
|
// result2 := intArrayEq.Equals([]int{1, 2, 3}, []int{1, 2, 4}) // false
|
||||||
|
func Eq[T any](e E.Eq[T]) E.Eq[[]T] {
|
||||||
|
eq := e.Equals
|
||||||
|
return E.FromEquals(func(left, right []T) bool {
|
||||||
|
return equals(left, right, eq)
|
||||||
|
})
|
||||||
|
}
|
||||||
44
v2/array/eq_test.go
Normal file
44
v2/array/eq_test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/eq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEq(t *testing.T) {
|
||||||
|
intEq := Eq(E.FromStrictEquals[int]())
|
||||||
|
|
||||||
|
// Test equal arrays
|
||||||
|
assert.True(t, intEq.Equals([]int{1, 2, 3}, []int{1, 2, 3}))
|
||||||
|
|
||||||
|
// Test different lengths
|
||||||
|
assert.False(t, intEq.Equals([]int{1, 2, 3}, []int{1, 2}))
|
||||||
|
|
||||||
|
// Test different values
|
||||||
|
assert.False(t, intEq.Equals([]int{1, 2, 3}, []int{1, 2, 4}))
|
||||||
|
|
||||||
|
// Test empty arrays
|
||||||
|
assert.True(t, intEq.Equals([]int{}, []int{}))
|
||||||
|
|
||||||
|
// Test string arrays
|
||||||
|
stringEq := Eq(E.FromStrictEquals[string]())
|
||||||
|
assert.True(t, stringEq.Equals([]string{"a", "b"}, []string{"a", "b"}))
|
||||||
|
assert.False(t, stringEq.Equals([]string{"a", "b"}, []string{"a", "c"}))
|
||||||
|
}
|
||||||
77
v2/array/example_any_test.go
Normal file
77
v2/array/example_any_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_any() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
fmt.Println(Any(pred)(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_any_filter() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
// Any tests if any of the entries in the array matches the condition
|
||||||
|
Any := F.Flow2(
|
||||||
|
Filter(pred),
|
||||||
|
IsNonEmpty[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Any(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_any_find() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
// Any tests if any of the entries in the array matches the condition
|
||||||
|
Any := F.Flow2(
|
||||||
|
FindFirst(pred),
|
||||||
|
O.IsSome[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Any(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
55
v2/array/example_find_test.go
Normal file
55
v2/array/example_find_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_find() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
fmt.Println(FindFirst(pred)(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_find_filter() {
|
||||||
|
|
||||||
|
pred := func(val int) bool {
|
||||||
|
return val&2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := From(1, 2, 3)
|
||||||
|
|
||||||
|
Find := F.Flow2(
|
||||||
|
Filter(pred),
|
||||||
|
Head[int],
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(Find(data1))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Some[int](1)
|
||||||
|
}
|
||||||
59
v2/array/examples_basic_test.go
Normal file
59
v2/array/examples_basic_test.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Example_basic adapts examples from [https://github.com/inato/fp-ts-cheatsheet#basic-manipulation]
|
||||||
|
func Example_basic() {
|
||||||
|
|
||||||
|
someArray := From(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) // []int
|
||||||
|
|
||||||
|
isEven := func(num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
square := func(num int) int {
|
||||||
|
return num * num
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter and map
|
||||||
|
result := F.Pipe2(
|
||||||
|
someArray,
|
||||||
|
Filter(isEven),
|
||||||
|
Map(square),
|
||||||
|
) // [0 4 16 36 64]
|
||||||
|
|
||||||
|
// or in one go with filterMap
|
||||||
|
resultFilterMap := F.Pipe1(
|
||||||
|
someArray,
|
||||||
|
FilterMap(
|
||||||
|
F.Flow2(O.FromPredicate(isEven), O.Map(square)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(resultFilterMap)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [0 4 16 36 64]
|
||||||
|
// [0 4 16 36 64]
|
||||||
|
}
|
||||||
92
v2/array/examples_sort_test.go
Normal file
92
v2/array/examples_sort_test.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
I "github.com/IBM/fp-go/v2/number/integer"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/ord"
|
||||||
|
S "github.com/IBM/fp-go/v2/string"
|
||||||
|
)
|
||||||
|
|
||||||
|
type user struct {
|
||||||
|
name string
|
||||||
|
age O.Option[int]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user user) GetName() string {
|
||||||
|
return user.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user user) GetAge() O.Option[int] {
|
||||||
|
return user.age
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example_sort adapts examples from [https://github.com/inato/fp-ts-cheatsheet#sort-elements-with-ord]
|
||||||
|
func Example_sort() {
|
||||||
|
|
||||||
|
strings := From("zyx", "abc", "klm")
|
||||||
|
|
||||||
|
sortedStrings := F.Pipe1(
|
||||||
|
strings,
|
||||||
|
Sort(S.Ord),
|
||||||
|
) // => ['abc', 'klm', 'zyx']
|
||||||
|
|
||||||
|
// reverse sort
|
||||||
|
reverseSortedStrings := F.Pipe1(
|
||||||
|
strings,
|
||||||
|
Sort(ord.Reverse(S.Ord)),
|
||||||
|
) // => ['zyx', 'klm', 'abc']
|
||||||
|
|
||||||
|
// sort Option
|
||||||
|
optionalNumbers := From(O.Some(1337), O.None[int](), O.Some(42))
|
||||||
|
|
||||||
|
sortedNums := F.Pipe1(
|
||||||
|
optionalNumbers,
|
||||||
|
Sort(O.Ord(I.Ord)),
|
||||||
|
)
|
||||||
|
|
||||||
|
// complex object with different rules
|
||||||
|
byName := F.Pipe1(
|
||||||
|
S.Ord,
|
||||||
|
ord.Contramap(user.GetName),
|
||||||
|
) // ord.Ord[user]
|
||||||
|
|
||||||
|
byAge := F.Pipe1(
|
||||||
|
O.Ord(I.Ord),
|
||||||
|
ord.Contramap(user.GetAge),
|
||||||
|
) // ord.Ord[user]
|
||||||
|
|
||||||
|
sortedUsers := F.Pipe1(
|
||||||
|
From(user{name: "a", age: O.Of(30)}, user{name: "d", age: O.Of(10)}, user{name: "c"}, user{name: "b", age: O.Of(10)}),
|
||||||
|
SortBy(From(byAge, byName)),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(sortedStrings)
|
||||||
|
fmt.Println(reverseSortedStrings)
|
||||||
|
fmt.Println(sortedNums)
|
||||||
|
fmt.Println(sortedUsers)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [abc klm zyx]
|
||||||
|
// [zyx klm abc]
|
||||||
|
// [None[int] Some[int](42) Some[int](1337)]
|
||||||
|
// [{c {false 0}} {b {true 10}} {d {true 10}} {a {true 30}}]
|
||||||
|
|
||||||
|
}
|
||||||
99
v2/array/find.go
Normal file
99
v2/array/find.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindFirst finds the first element which satisfies a predicate function.
|
||||||
|
// Returns Some(element) if found, None if no element matches.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// findGreaterThan3 := array.FindFirst(func(x int) bool { return x > 3 })
|
||||||
|
// result := findGreaterThan3([]int{1, 2, 4, 5}) // Some(4)
|
||||||
|
// result2 := findGreaterThan3([]int{1, 2, 3}) // None
|
||||||
|
func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindFirst[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstWithIndex finds the first element which satisfies a predicate function that also receives the index.
|
||||||
|
// Returns Some(element) if found, None if no element matches.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// findEvenAtEvenIndex := array.FindFirstWithIndex(func(i, x int) bool {
|
||||||
|
// return i%2 == 0 && x%2 == 0
|
||||||
|
// })
|
||||||
|
// result := findEvenAtEvenIndex([]int{1, 3, 4, 5}) // Some(4)
|
||||||
|
func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindFirstWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMap finds the first element for which the selector function returns Some.
|
||||||
|
// This combines finding and mapping in a single operation.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "strconv"
|
||||||
|
//
|
||||||
|
// parseFirst := array.FindFirstMap(func(s string) option.Option[int] {
|
||||||
|
// if n, err := strconv.Atoi(s); err == nil {
|
||||||
|
// return option.Some(n)
|
||||||
|
// }
|
||||||
|
// return option.None[int]()
|
||||||
|
// })
|
||||||
|
// result := parseFirst([]string{"a", "42", "b"}) // Some(42)
|
||||||
|
func FindFirstMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindFirstMap[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMapWithIndex finds the first element for which the selector function returns Some.
|
||||||
|
// The selector receives both the index and the element.
|
||||||
|
func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindFirstMapWithIndex[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLast finds the last element which satisfies a predicate function.
|
||||||
|
// Returns Some(element) if found, None if no element matches.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// findGreaterThan3 := array.FindLast(func(x int) bool { return x > 3 })
|
||||||
|
// result := findGreaterThan3([]int{1, 4, 2, 5}) // Some(5)
|
||||||
|
func FindLast[A any](pred func(A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindLast[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastWithIndex finds the last element which satisfies a predicate function that also receives the index.
|
||||||
|
// Returns Some(element) if found, None if no element matches.
|
||||||
|
func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
||||||
|
return G.FindLastWithIndex[[]A](pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMap finds the last element for which the selector function returns Some.
|
||||||
|
// This combines finding and mapping in a single operation, searching from the end.
|
||||||
|
func FindLastMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindLastMap[[]A](sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMapWithIndex finds the last element for which the selector function returns Some.
|
||||||
|
// The selector receives both the index and the element, searching from the end.
|
||||||
|
func FindLastMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
|
return G.FindLastMapWithIndex[[]A](sel)
|
||||||
|
}
|
||||||
105
v2/array/find_test.go
Normal file
105
v2/array/find_test.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindFirstWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
finder := FindFirstWithIndex(func(i, x int) bool {
|
||||||
|
return i > 2 && x%2 == 0
|
||||||
|
})
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some(4), result)
|
||||||
|
|
||||||
|
notFound := FindFirstWithIndex(func(i, x int) bool {
|
||||||
|
return i > 10
|
||||||
|
})
|
||||||
|
assert.Equal(t, O.None[int](), notFound(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindFirstMap(t *testing.T) {
|
||||||
|
src := []string{"a", "42", "b", "100"}
|
||||||
|
finder := FindFirstMap(func(s string) O.Option[int] {
|
||||||
|
if len(s) > 1 {
|
||||||
|
return O.Some(len(s))
|
||||||
|
}
|
||||||
|
return O.None[int]()
|
||||||
|
})
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some(2), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindFirstMapWithIndex(t *testing.T) {
|
||||||
|
src := []string{"a", "b", "c", "d"}
|
||||||
|
finder := FindFirstMapWithIndex(func(i int, s string) O.Option[string] {
|
||||||
|
if i > 1 {
|
||||||
|
return O.Some(fmt.Sprintf("%d:%s", i, s))
|
||||||
|
}
|
||||||
|
return O.None[string]()
|
||||||
|
})
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some("2:c"), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLast(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
finder := FindLast(func(x int) bool { return x%2 == 0 })
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some(4), result)
|
||||||
|
|
||||||
|
notFound := FindLast(func(x int) bool { return x > 10 })
|
||||||
|
assert.Equal(t, O.None[int](), notFound(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLastWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
finder := FindLastWithIndex(func(i, x int) bool {
|
||||||
|
return i < 3 && x%2 == 0
|
||||||
|
})
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some(2), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLastMap(t *testing.T) {
|
||||||
|
src := []string{"a", "42", "b", "100"}
|
||||||
|
finder := FindLastMap(func(s string) O.Option[int] {
|
||||||
|
if len(s) > 1 {
|
||||||
|
return O.Some(len(s))
|
||||||
|
}
|
||||||
|
return O.None[int]()
|
||||||
|
})
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some(3), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLastMapWithIndex(t *testing.T) {
|
||||||
|
src := []string{"a", "b", "c", "d"}
|
||||||
|
finder := FindLastMapWithIndex(func(i int, s string) O.Option[string] {
|
||||||
|
if i < 3 {
|
||||||
|
return O.Some(fmt.Sprintf("%d:%s", i, s))
|
||||||
|
}
|
||||||
|
return O.None[string]()
|
||||||
|
})
|
||||||
|
result := finder(src)
|
||||||
|
assert.Equal(t, O.Some("2:c"), result)
|
||||||
|
}
|
||||||
34
v2/array/generic/any.go
Normal file
34
v2/array/generic/any.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnyWithIndex tests if any of the elements in the array matches the predicate
|
||||||
|
func AnyWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) bool {
|
||||||
|
return F.Flow2(
|
||||||
|
FindFirstWithIndex[AS](pred),
|
||||||
|
O.IsSome[A],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any tests if any of the elements in the array matches the predicate
|
||||||
|
func Any[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) bool {
|
||||||
|
return AnyWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
368
v2/array/generic/array.go
Normal file
368
v2/array/generic/array.go
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/array"
|
||||||
|
FC "github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Of constructs a single element array
|
||||||
|
func Of[GA ~[]A, A any](value A) GA {
|
||||||
|
return GA{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduce[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceWithIndex[GA ~[]A, A, B any](f func(int, B, A) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceWithIndex[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[GA ~[]A, A, B any](f func(A, B) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceRight[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRightWithIndex[GA ~[]A, A, B any](f func(int, A, B) B, initial B) func(GA) B {
|
||||||
|
return func(as GA) B {
|
||||||
|
return MonadReduceRightWithIndex[GA](as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
|
||||||
|
return array.Reduce(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
|
||||||
|
return array.ReduceWithIndex(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduceRight[GA ~[]A, A, B any](fa GA, f func(A, B) B, initial B) B {
|
||||||
|
return array.ReduceRight(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadReduceRightWithIndex[GA ~[]A, A, B any](fa GA, f func(int, A, B) B, initial B) B {
|
||||||
|
return array.ReduceRightWithIndex(fa, f, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From constructs an array from a set of variadic arguments
|
||||||
|
func From[GA ~[]A, A any](data ...A) GA {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`.
|
||||||
|
func MakeBy[AS ~[]A, F ~func(int) A, A any](n int, f F) AS {
|
||||||
|
// sanity check
|
||||||
|
if n <= 0 {
|
||||||
|
return Empty[AS]()
|
||||||
|
}
|
||||||
|
// run the generator function across the input
|
||||||
|
as := make(AS, n)
|
||||||
|
for i := n - 1; i >= 0; i-- {
|
||||||
|
as[i] = f(i)
|
||||||
|
}
|
||||||
|
return as
|
||||||
|
}
|
||||||
|
|
||||||
|
func Replicate[AS ~[]A, A any](n int, a A) AS {
|
||||||
|
return MakeBy[AS](n, F.Constant1[int](a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Lookup[GA ~[]A, A any](idx int) func(GA) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
if idx < 0 {
|
||||||
|
return F.Constant1[GA](none)
|
||||||
|
}
|
||||||
|
return func(as GA) O.Option[A] {
|
||||||
|
if idx < len(as) {
|
||||||
|
return O.Some(as[idx])
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tail[GA ~[]A, A any](as GA) O.Option[GA] {
|
||||||
|
if array.IsEmpty(as) {
|
||||||
|
return O.None[GA]()
|
||||||
|
}
|
||||||
|
return O.Some(as[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func Head[GA ~[]A, A any](as GA) O.Option[A] {
|
||||||
|
if array.IsEmpty(as) {
|
||||||
|
return O.None[A]()
|
||||||
|
}
|
||||||
|
return O.Some(as[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func First[GA ~[]A, A any](as GA) O.Option[A] {
|
||||||
|
return Head(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Last[GA ~[]A, A any](as GA) O.Option[A] {
|
||||||
|
if array.IsEmpty(as) {
|
||||||
|
return O.None[A]()
|
||||||
|
}
|
||||||
|
return O.Some(as[len(as)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func Append[GA ~[]A, A any](as GA, a A) GA {
|
||||||
|
return array.Append(as, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Empty[GA ~[]A, A any]() GA {
|
||||||
|
return array.Empty[GA]()
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpsertAt[GA ~[]A, A any](a A) func(GA) GA {
|
||||||
|
return array.UpsertAt[GA](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
||||||
|
return array.MonadMap[GA, GB](as, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
||||||
|
return array.Map[GA, GB](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB {
|
||||||
|
return array.MonadMapWithIndex[GA, GB](as, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) B) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadMapWithIndex[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Size[GA ~[]A, A any](as GA) int {
|
||||||
|
return len(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB {
|
||||||
|
result := make(GB, 0, len(fa))
|
||||||
|
for _, a := range fa {
|
||||||
|
O.Map(func(b B) B {
|
||||||
|
result = append(result, b)
|
||||||
|
return b
|
||||||
|
})(f(a))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
|
||||||
|
result := make(GB, 0, len(fa))
|
||||||
|
for i, a := range fa {
|
||||||
|
O.Map(func(b B) B {
|
||||||
|
result = append(result, b)
|
||||||
|
return b
|
||||||
|
})(f(i, a))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB {
|
||||||
|
return filterMap[GA, GB](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
|
||||||
|
return filterMapWithIndex[GA, GB](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](fa AS, pred PRED) AS {
|
||||||
|
result := make(AS, 0, len(fa))
|
||||||
|
for i, a := range fa {
|
||||||
|
if pred(i, a) {
|
||||||
|
result = append(result, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) AS {
|
||||||
|
return F.Bind2nd(filterWithIndex[AS, PRED, A], pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Filter[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) AS {
|
||||||
|
return FilterWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterChain[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[GB]) func(GA) GB {
|
||||||
|
return F.Flow2(
|
||||||
|
FilterMap[GA, []GB](f),
|
||||||
|
Flatten[[]GB],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[GAA ~[]GA, GA ~[]A, A any](mma GAA) GA {
|
||||||
|
return MonadChain(mma, F.Identity[GA])
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(A) O.Option[B]) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) O.Option[B]) func(GA) GB {
|
||||||
|
return F.Bind2nd(MonadFilterMapWithIndex[GA, GB, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
||||||
|
left := Empty[GA]()
|
||||||
|
right := Empty[GA]()
|
||||||
|
array.Reduce(as, func(c bool, a A) bool {
|
||||||
|
if pred(a) {
|
||||||
|
right = append(right, a)
|
||||||
|
} else {
|
||||||
|
left = append(left, a)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}, true)
|
||||||
|
// returns the partition
|
||||||
|
return tuple.MakeTuple2(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Partition[GA ~[]A, A any](pred func(A) bool) func(GA) tuple.Tuple2[GA, GA] {
|
||||||
|
return F.Bind2nd(MonadPartition[GA, A], pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[AS ~[]A, BS ~[]B, A, B any](fa AS, f func(a A) BS) BS {
|
||||||
|
return array.Reduce(fa, func(bs BS, a A) BS {
|
||||||
|
return append(bs, f(a)...)
|
||||||
|
}, Empty[BS]())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[AS ~[]A, BS ~[]B, A, B any](f func(A) BS) func(AS) BS {
|
||||||
|
return F.Bind2nd(MonadChain[AS, BS, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fab ABS, fa AS) BS {
|
||||||
|
return MonadChain(fab, F.Bind1st(MonadMap[AS, BS, A, B], fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fa AS) func(ABS) BS {
|
||||||
|
return F.Bind2nd(MonadAp[BS, ABS, AS], fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsEmpty[AS ~[]A, A any](as AS) bool {
|
||||||
|
return array.IsEmpty(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNil[GA ~[]A, A any](as GA) bool {
|
||||||
|
return array.IsNil(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNonNil[GA ~[]A, A any](as GA) bool {
|
||||||
|
return array.IsNonNil(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
if IsEmpty(as) {
|
||||||
|
return onEmpty()
|
||||||
|
}
|
||||||
|
return onNonEmpty(as)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchLeft[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(A, AS) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
if IsEmpty(as) {
|
||||||
|
return onEmpty()
|
||||||
|
}
|
||||||
|
return onNonEmpty(as[0], as[1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Slice[AS ~[]A, A any](start int, end int) func(AS) AS {
|
||||||
|
return func(a AS) AS {
|
||||||
|
return a[start:end]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceRight[AS ~[]A, A any](start int) func(AS) AS {
|
||||||
|
return func(a AS) AS {
|
||||||
|
return a[start:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Copy[AS ~[]A, A any](b AS) AS {
|
||||||
|
buf := make(AS, len(b))
|
||||||
|
copy(buf, b)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func Clone[AS ~[]A, A any](f func(A) A) func(as AS) AS {
|
||||||
|
// implementation assumes that map does not optimize for the empty array
|
||||||
|
return Map[AS, AS](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
||||||
|
empty := m.Empty()
|
||||||
|
concat := m.Concat
|
||||||
|
return func(f func(A) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
return array.Reduce(as, func(cur B, a A) B {
|
||||||
|
return concat(cur, f(a))
|
||||||
|
}, empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FoldMapWithIndex[AS ~[]A, A, B any](m M.Monoid[B]) func(func(int, A) B) func(AS) B {
|
||||||
|
empty := m.Empty()
|
||||||
|
concat := m.Concat
|
||||||
|
return func(f func(int, A) B) func(AS) B {
|
||||||
|
return func(as AS) B {
|
||||||
|
return array.ReduceWithIndex(as, func(idx int, cur B, a A) B {
|
||||||
|
return concat(cur, f(idx, a))
|
||||||
|
}, empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
||||||
|
empty := m.Empty()
|
||||||
|
concat := m.Concat
|
||||||
|
return func(as AS) A {
|
||||||
|
return array.Reduce(as, concat, empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Push[ENDO ~func(GA) GA, GA ~[]A, A any](a A) ENDO {
|
||||||
|
return F.Bind2nd(array.Push[GA, A], a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB {
|
||||||
|
return FC.MonadFlap(MonadMap[GFAB, GB], fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB {
|
||||||
|
return FC.Flap(Map[GFAB, GB], a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Prepend[ENDO ~func(AS) AS, AS []A, A any](head A) ENDO {
|
||||||
|
return array.Prepend[ENDO](head)
|
||||||
|
}
|
||||||
89
v2/array/generic/bind.go
Normal file
89
v2/array/generic/bind.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
C "github.com/IBM/fp-go/v2/internal/chain"
|
||||||
|
F "github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[GS ~[]S, S any](
|
||||||
|
empty S,
|
||||||
|
) GS {
|
||||||
|
return Of[GS](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) GT,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return C.Bind(
|
||||||
|
Chain[GS1, GS2, S1, S2],
|
||||||
|
Map[GT, GS2, T, S2],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[GS1 ~[]S1, GS2 ~[]S2, S1, S2, T any](
|
||||||
|
key func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return F.Let(
|
||||||
|
Map[GS1, GS2, S1, S2],
|
||||||
|
key,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[GS1 ~[]S1, GS2 ~[]S2, S1, S2, B any](
|
||||||
|
key func(B) func(S1) S2,
|
||||||
|
b B,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return F.LetTo(
|
||||||
|
Map[GS1, GS2, S1, S2],
|
||||||
|
key,
|
||||||
|
b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[GS1 ~[]S1, GT ~[]T, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(GT) GS1 {
|
||||||
|
return C.BindTo(
|
||||||
|
Map[GT, GS1, T, S1],
|
||||||
|
setter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa GT,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return A.ApS(
|
||||||
|
Ap[GS2, []func(T) S2, GT, S2, T],
|
||||||
|
Map[GS1, []func(T) S2, S1, func(T) S2],
|
||||||
|
setter,
|
||||||
|
fa,
|
||||||
|
)
|
||||||
|
}
|
||||||
97
v2/array/generic/find.go
Normal file
97
v2/array/generic/find.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirstWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
return func(as AS) O.Option[A] {
|
||||||
|
for i, a := range as {
|
||||||
|
if pred(i, a) {
|
||||||
|
return O.Some(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirst finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindFirst[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
return FindFirstWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
none := O.None[B]()
|
||||||
|
return func(as AS) O.Option[B] {
|
||||||
|
count := len(as)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
out := pred(i, as[i])
|
||||||
|
if O.IsSome(out) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindFirstMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
return FindFirstMapWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastWithIndex finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLastWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
none := O.None[A]()
|
||||||
|
return func(as AS) O.Option[A] {
|
||||||
|
for i := len(as) - 1; i >= 0; i-- {
|
||||||
|
a := as[i]
|
||||||
|
if pred(i, a) {
|
||||||
|
return O.Some(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLast finds the first element which satisfies a predicate (or a refinement) function
|
||||||
|
func FindLast[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] {
|
||||||
|
return FindLastWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMapWithIndex finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
none := O.None[B]()
|
||||||
|
return func(as AS) O.Option[B] {
|
||||||
|
for i := len(as) - 1; i >= 0; i-- {
|
||||||
|
out := pred(i, as[i])
|
||||||
|
if O.IsSome(out) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastMap finds the first element returned by an [O.Option] based selector function
|
||||||
|
func FindLastMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] {
|
||||||
|
return FindLastMapWithIndex[AS](F.Ignore1of2[int](pred))
|
||||||
|
}
|
||||||
43
v2/array/generic/monad.go
Normal file
43
v2/array/generic/monad.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2024 - 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/internal/monad"
|
||||||
|
)
|
||||||
|
|
||||||
|
type arrayMonad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B] struct{}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Of(a A) GA {
|
||||||
|
return Of[GA, A](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB {
|
||||||
|
return Map[GA, GB, A, B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB {
|
||||||
|
return Chain[GA, GB, A, B](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
|
||||||
|
return Ap[GB, GAB, GA, B, A](fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monad implements the monadic operations for an array
|
||||||
|
func Monad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B]() monad.Monad[A, B, GA, GB, GAB] {
|
||||||
|
return &arrayMonad[A, B, GA, GB, GAB]{}
|
||||||
|
}
|
||||||
56
v2/array/generic/sort.go
Normal file
56
v2/array/generic/sort.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
O "github.com/IBM/fp-go/v2/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort implements a stable sort on the array given the provided ordering
|
||||||
|
func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
|
||||||
|
return SortByKey[GA](ord, F.Identity[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortByKey implements a stable sort on the array given the provided ordering on an extracted key
|
||||||
|
func SortByKey[GA ~[]T, K, T any](ord O.Ord[K], f func(T) K) func(ma GA) GA {
|
||||||
|
|
||||||
|
return func(ma GA) GA {
|
||||||
|
// nothing to sort
|
||||||
|
l := len(ma)
|
||||||
|
if l < 2 {
|
||||||
|
return ma
|
||||||
|
}
|
||||||
|
// copy
|
||||||
|
cpy := make(GA, l)
|
||||||
|
copy(cpy, ma)
|
||||||
|
sort.Slice(cpy, func(i, j int) bool {
|
||||||
|
return ord.Compare(f(cpy[i]), f(cpy[j])) < 0
|
||||||
|
})
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortBy implements a stable sort on the array given the provided ordering
|
||||||
|
func SortBy[GA ~[]T, GO ~[]O.Ord[T], T any](ord GO) func(ma GA) GA {
|
||||||
|
return F.Pipe2(
|
||||||
|
ord,
|
||||||
|
Fold[GO](O.Monoid[T]()),
|
||||||
|
Sort[GA, T],
|
||||||
|
)
|
||||||
|
}
|
||||||
32
v2/array/generic/uniq.go
Normal file
32
v2/array/generic/uniq.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package generic
|
||||||
|
|
||||||
|
import F "github.com/IBM/fp-go/v2/function"
|
||||||
|
|
||||||
|
// StrictUniq converts an array of arbitrary items into an array or unique items
|
||||||
|
// where uniqueness is determined by the built-in uniqueness constraint
|
||||||
|
func StrictUniq[AS ~[]A, A comparable](as AS) AS {
|
||||||
|
return Uniq[AS](F.Identity[A])(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// uniquePredUnsafe returns a predicate on a map for uniqueness
|
||||||
|
func uniquePredUnsafe[PRED ~func(A) K, A any, K comparable](f PRED) func(int, A) bool {
|
||||||
|
lookup := make(map[K]bool)
|
||||||
|
return func(_ int, a A) bool {
|
||||||
|
k := f(a)
|
||||||
|
_, has := lookup[k]
|
||||||
|
if has {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
lookup[k] = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uniq converts an array of arbitrary items into an array or unique items
|
||||||
|
// where uniqueness is determined based on a key extractor function
|
||||||
|
func Uniq[AS ~[]A, PRED ~func(A) K, A any, K comparable](f PRED) func(as AS) AS {
|
||||||
|
return func(as AS) AS {
|
||||||
|
// we need to create a new predicate for each iteration
|
||||||
|
return filterWithIndex(as, uniquePredUnsafe(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
52
v2/array/generic/zip.go
Normal file
52
v2/array/generic/zip.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
N "github.com/IBM/fp-go/v2/number"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZipWith applies a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one
|
||||||
|
// input array is short, excess elements of the longer array are discarded.
|
||||||
|
func ZipWith[AS ~[]A, BS ~[]B, CS ~[]C, FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS {
|
||||||
|
l := N.Min(len(fa), len(fb))
|
||||||
|
res := make(CS, l)
|
||||||
|
for i := l - 1; i >= 0; i-- {
|
||||||
|
res[i] = f(fa[i], fb[i])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zip takes two arrays and returns an array of corresponding pairs. If one input array is short, excess elements of the
|
||||||
|
// longer array are discarded
|
||||||
|
func Zip[AS ~[]A, BS ~[]B, CS ~[]T.Tuple2[A, B], A, B any](fb BS) func(AS) CS {
|
||||||
|
return F.Bind23of3(ZipWith[AS, BS, CS, func(A, B) T.Tuple2[A, B]])(fb, T.MakeTuple2[A, B])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip is the function is reverse of [Zip]. Takes an array of pairs and return two corresponding arrays
|
||||||
|
func Unzip[AS ~[]A, BS ~[]B, CS ~[]T.Tuple2[A, B], A, B any](cs CS) T.Tuple2[AS, BS] {
|
||||||
|
l := len(cs)
|
||||||
|
as := make(AS, l)
|
||||||
|
bs := make(BS, l)
|
||||||
|
for i := l - 1; i >= 0; i-- {
|
||||||
|
t := cs[i]
|
||||||
|
as[i] = t.F1
|
||||||
|
bs[i] = t.F2
|
||||||
|
}
|
||||||
|
return T.MakeTuple2(as, bs)
|
||||||
|
}
|
||||||
38
v2/array/magma.go
Normal file
38
v2/array/magma.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConcatAll concatenates all elements of an array using the provided Monoid.
|
||||||
|
// This reduces the array to a single value by repeatedly applying the Monoid's concat operation.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
//
|
||||||
|
// // Sum all numbers
|
||||||
|
// sumAll := array.ConcatAll(monoid.MonoidSum[int]())
|
||||||
|
// result := sumAll([]int{1, 2, 3, 4, 5}) // 15
|
||||||
|
//
|
||||||
|
// // Concatenate all strings
|
||||||
|
// concatStrings := array.ConcatAll(monoid.MonoidString())
|
||||||
|
// result2 := concatStrings([]string{"Hello", " ", "World"}) // "Hello World"
|
||||||
|
func ConcatAll[A any](m M.Monoid[A]) func([]A) A {
|
||||||
|
return Reduce(m.Concat, m.Empty())
|
||||||
|
}
|
||||||
36
v2/array/magma_test.go
Normal file
36
v2/array/magma_test.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var subInt = M.MakeMonoid(func(first int, second int) int {
|
||||||
|
return first - second
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
func TestConcatAll(t *testing.T) {
|
||||||
|
|
||||||
|
var subAll = ConcatAll(subInt)
|
||||||
|
|
||||||
|
assert.Equal(t, subAll([]int{1, 2, 3}), -6)
|
||||||
|
|
||||||
|
}
|
||||||
161
v2/array/misc_test.go
Normal file
161
v2/array/misc_test.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
OR "github.com/IBM/fp-go/v2/ord"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnyWithIndex(t *testing.T) {
|
||||||
|
src := []int{1, 2, 3, 4, 5}
|
||||||
|
checker := AnyWithIndex(func(i, x int) bool {
|
||||||
|
return i == 2 && x == 3
|
||||||
|
})
|
||||||
|
assert.True(t, checker(src))
|
||||||
|
|
||||||
|
checker2 := AnyWithIndex(func(i, x int) bool {
|
||||||
|
return i == 10
|
||||||
|
})
|
||||||
|
assert.False(t, checker2(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSemigroup(t *testing.T) {
|
||||||
|
sg := Semigroup[int]()
|
||||||
|
result := sg.Concat([]int{1, 2}, []int{3, 4})
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayConcatAll(t *testing.T) {
|
||||||
|
result := ArrayConcatAll(
|
||||||
|
[]int{1, 2},
|
||||||
|
[]int{3, 4},
|
||||||
|
[]int{5, 6},
|
||||||
|
)
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4, 5, 6}, result)
|
||||||
|
|
||||||
|
// Test with empty arrays
|
||||||
|
result2 := ArrayConcatAll(
|
||||||
|
[]int{},
|
||||||
|
[]int{1},
|
||||||
|
[]int{},
|
||||||
|
)
|
||||||
|
assert.Equal(t, []int{1}, result2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonad(t *testing.T) {
|
||||||
|
m := Monad[int, string]()
|
||||||
|
|
||||||
|
// Test Map
|
||||||
|
mapFn := m.Map(func(x int) string {
|
||||||
|
return string(rune('a' + x - 1))
|
||||||
|
})
|
||||||
|
mapped := mapFn([]int{1, 2, 3})
|
||||||
|
assert.Equal(t, []string{"a", "b", "c"}, mapped)
|
||||||
|
|
||||||
|
// Test Chain
|
||||||
|
chainFn := m.Chain(func(x int) []string {
|
||||||
|
return []string{string(rune('a' + x - 1))}
|
||||||
|
})
|
||||||
|
chained := chainFn([]int{1, 2})
|
||||||
|
assert.Equal(t, []string{"a", "b"}, chained)
|
||||||
|
|
||||||
|
// Test Of
|
||||||
|
ofResult := m.Of(42)
|
||||||
|
assert.Equal(t, []int{42}, ofResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortByKey(t *testing.T) {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{"Alice", 30},
|
||||||
|
{"Bob", 25},
|
||||||
|
{"Charlie", 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
sorter := SortByKey(OR.FromStrictCompare[int](), func(p Person) int {
|
||||||
|
return p.Age
|
||||||
|
})
|
||||||
|
result := sorter(people)
|
||||||
|
|
||||||
|
assert.Equal(t, "Bob", result[0].Name)
|
||||||
|
assert.Equal(t, "Alice", result[1].Name)
|
||||||
|
assert.Equal(t, "Charlie", result[2].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadTraverse(t *testing.T) {
|
||||||
|
result := MonadTraverse(
|
||||||
|
O.Of[[]int],
|
||||||
|
O.Map[[]int, func(int) []int],
|
||||||
|
O.Ap[[]int, int],
|
||||||
|
[]int{1, 3, 5},
|
||||||
|
func(n int) O.Option[int] {
|
||||||
|
if n%2 == 1 {
|
||||||
|
return O.Some(n * 2)
|
||||||
|
}
|
||||||
|
return O.None[int]()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, O.Some([]int{2, 6, 10}), result)
|
||||||
|
|
||||||
|
// Test with None case
|
||||||
|
result2 := MonadTraverse(
|
||||||
|
O.Of[[]int],
|
||||||
|
O.Map[[]int, func(int) []int],
|
||||||
|
O.Ap[[]int, int],
|
||||||
|
[]int{1, 2, 3},
|
||||||
|
func(n int) O.Option[int] {
|
||||||
|
if n%2 == 1 {
|
||||||
|
return O.Some(n * 2)
|
||||||
|
}
|
||||||
|
return O.None[int]()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, O.None[[]int](), result2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUniqByKey(t *testing.T) {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{"Alice", 30},
|
||||||
|
{"Bob", 25},
|
||||||
|
{"Alice", 35},
|
||||||
|
{"Charlie", 30},
|
||||||
|
}
|
||||||
|
|
||||||
|
uniquer := Uniq(func(p Person) string {
|
||||||
|
return p.Name
|
||||||
|
})
|
||||||
|
result := uniquer(people)
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(result))
|
||||||
|
assert.Equal(t, "Alice", result[0].Name)
|
||||||
|
assert.Equal(t, "Bob", result[1].Name)
|
||||||
|
assert.Equal(t, "Charlie", result[2].Name)
|
||||||
|
}
|
||||||
39
v2/array/monad.go
Normal file
39
v2/array/monad.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2024 - 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/monad"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Monad returns the monadic operations for an array.
|
||||||
|
// This provides a structured way to access all monad operations (Map, Chain, Ap, Of)
|
||||||
|
// for arrays in a single interface.
|
||||||
|
//
|
||||||
|
// The Monad interface is useful when you need to pass monadic operations as parameters
|
||||||
|
// or when working with generic code that operates on any monad.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// m := array.Monad[int, string]()
|
||||||
|
// result := m.Chain([]int{1, 2, 3}, func(x int) []string {
|
||||||
|
// return []string{fmt.Sprintf("%d", x), fmt.Sprintf("%d!", x)}
|
||||||
|
// })
|
||||||
|
// // Result: ["1", "1!", "2", "2!", "3", "3!"]
|
||||||
|
func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] {
|
||||||
|
return G.Monad[A, B, []A, []B, []func(A) B]()
|
||||||
|
}
|
||||||
89
v2/array/monoid.go
Normal file
89
v2/array/monoid.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/internal/array"
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
S "github.com/IBM/fp-go/v2/semigroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func concat[T any](left, right []T) []T {
|
||||||
|
// some performance checks
|
||||||
|
ll := len(left)
|
||||||
|
if ll == 0 {
|
||||||
|
return right
|
||||||
|
}
|
||||||
|
lr := len(right)
|
||||||
|
if lr == 0 {
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
// need to copy
|
||||||
|
buf := make([]T, ll+lr)
|
||||||
|
copy(buf[copy(buf, left):], right)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monoid returns a Monoid instance for arrays.
|
||||||
|
// The Monoid combines arrays through concatenation, with an empty array as the identity element.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// m := array.Monoid[int]()
|
||||||
|
// result := m.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
|
||||||
|
// empty := m.Empty() // []
|
||||||
|
func Monoid[T any]() M.Monoid[[]T] {
|
||||||
|
return M.MakeMonoid(concat[T], Empty[T]())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Semigroup returns a Semigroup instance for arrays.
|
||||||
|
// The Semigroup combines arrays through concatenation.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// s := array.Semigroup[int]()
|
||||||
|
// result := s.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
|
||||||
|
func Semigroup[T any]() S.Semigroup[[]T] {
|
||||||
|
return S.MakeSemigroup(concat[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLen[A any](count int, data []A) int {
|
||||||
|
return count + len(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayConcatAll efficiently concatenates multiple arrays into a single array.
|
||||||
|
// This function pre-allocates the exact amount of memory needed and performs
|
||||||
|
// a single copy operation for each input array, making it more efficient than
|
||||||
|
// repeated concatenations.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := array.ArrayConcatAll(
|
||||||
|
// []int{1, 2},
|
||||||
|
// []int{3, 4},
|
||||||
|
// []int{5, 6},
|
||||||
|
// ) // [1, 2, 3, 4, 5, 6]
|
||||||
|
func ArrayConcatAll[A any](data ...[]A) []A {
|
||||||
|
// get the full size
|
||||||
|
count := array.Reduce(data, addLen[A], 0)
|
||||||
|
buf := make([]A, count)
|
||||||
|
// copy
|
||||||
|
array.Reduce(data, func(idx int, seg []A) int {
|
||||||
|
return idx + copy(buf[idx:], seg)
|
||||||
|
}, 0)
|
||||||
|
// returns the final array
|
||||||
|
return buf
|
||||||
|
}
|
||||||
26
v2/array/monoid_test.go
Normal file
26
v2/array/monoid_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMonoid(t *testing.T) {
|
||||||
|
M.AssertLaws(t, Monoid[int]())([][]int{{}, {1}, {1, 2}})
|
||||||
|
}
|
||||||
136
v2/array/nonempty/array.go
Normal file
136
v2/array/nonempty/array.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// 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 nonempty
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
EM "github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/array"
|
||||||
|
S "github.com/IBM/fp-go/v2/semigroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NonEmptyArray represents an array with at least one element
|
||||||
|
type NonEmptyArray[A any] []A
|
||||||
|
|
||||||
|
// Of constructs a single element array
|
||||||
|
func Of[A any](first A) NonEmptyArray[A] {
|
||||||
|
return G.Of[NonEmptyArray[A]](first)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From constructs a [NonEmptyArray] from a set of variadic arguments
|
||||||
|
func From[A any](first A, data ...A) NonEmptyArray[A] {
|
||||||
|
count := len(data)
|
||||||
|
if count == 0 {
|
||||||
|
return Of(first)
|
||||||
|
}
|
||||||
|
// allocate the requested buffer
|
||||||
|
buffer := make(NonEmptyArray[A], count+1)
|
||||||
|
buffer[0] = first
|
||||||
|
copy(buffer[1:], data)
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsEmpty[A any](_ NonEmptyArray[A]) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNonEmpty[A any](_ NonEmptyArray[A]) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[A, B any](as NonEmptyArray[A], f func(a A) B) NonEmptyArray[B] {
|
||||||
|
return G.MonadMap[NonEmptyArray[A], NonEmptyArray[B]](as, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[A, B any](f func(a A) B) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
return F.Bind2nd(MonadMap[A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reduce[A, B any](f func(B, A) B, initial B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(as NonEmptyArray[A]) B {
|
||||||
|
return array.Reduce(as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReduceRight[A, B any](f func(A, B) B, initial B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(as NonEmptyArray[A]) B {
|
||||||
|
return array.ReduceRight(as, f, initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tail[A any](as NonEmptyArray[A]) []A {
|
||||||
|
return as[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Head[A any](as NonEmptyArray[A]) A {
|
||||||
|
return as[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func First[A any](as NonEmptyArray[A]) A {
|
||||||
|
return as[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Last[A any](as NonEmptyArray[A]) A {
|
||||||
|
return as[len(as)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Size[A any](as NonEmptyArray[A]) int {
|
||||||
|
return G.Size(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten[A any](mma NonEmptyArray[NonEmptyArray[A]]) NonEmptyArray[A] {
|
||||||
|
return G.Flatten(mma)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[A, B any](fa NonEmptyArray[A], f func(a A) NonEmptyArray[B]) NonEmptyArray[B] {
|
||||||
|
return G.MonadChain[NonEmptyArray[A], NonEmptyArray[B]](fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[A, B any](f func(A) NonEmptyArray[B]) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
return G.Chain[NonEmptyArray[A], NonEmptyArray[B]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[B, A any](fab NonEmptyArray[func(A) B], fa NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
return G.MonadAp[NonEmptyArray[B]](fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[B, A any](fa NonEmptyArray[A]) func(NonEmptyArray[func(A) B]) NonEmptyArray[B] {
|
||||||
|
return G.Ap[NonEmptyArray[B], NonEmptyArray[func(A) B]](fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FoldMap maps and folds a [NonEmptyArray]. Map the [NonEmptyArray] passing each value to the iterating function. Then fold the results using the provided [Semigroup].
|
||||||
|
func FoldMap[A, B any](s S.Semigroup[B]) func(func(A) B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(f func(A) B) func(NonEmptyArray[A]) B {
|
||||||
|
return func(as NonEmptyArray[A]) B {
|
||||||
|
return array.Reduce(Tail(as), func(cur B, a A) B {
|
||||||
|
return s.Concat(cur, f(a))
|
||||||
|
}, f(Head(as)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold folds the [NonEmptyArray] using the provided [Semigroup].
|
||||||
|
func Fold[A any](s S.Semigroup[A]) func(NonEmptyArray[A]) A {
|
||||||
|
return func(as NonEmptyArray[A]) A {
|
||||||
|
return array.Reduce(Tail(as), s.Concat, Head(as))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend prepends a single value to an array
|
||||||
|
func Prepend[A any](head A) EM.Endomorphism[NonEmptyArray[A]] {
|
||||||
|
return array.Prepend[EM.Endomorphism[NonEmptyArray[A]]](head)
|
||||||
|
}
|
||||||
95
v2/array/sequence.go
Normal file
95
v2/array/sequence.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sequence takes an array where elements are HKT<A> (higher kinded type) and,
|
||||||
|
// using an applicative of that HKT, returns an HKT of []A.
|
||||||
|
//
|
||||||
|
// For example, it can turn:
|
||||||
|
// - []Either[error, string] into Either[error, []string]
|
||||||
|
// - []Option[int] into Option[[]int]
|
||||||
|
//
|
||||||
|
// Sequence requires an Applicative of the HKT you are targeting. To turn an
|
||||||
|
// []Either[E, A] into an Either[E, []A], it needs an Applicative for Either.
|
||||||
|
// To turn an []Option[A] into an Option[[]A], it needs an Applicative for Option.
|
||||||
|
//
|
||||||
|
// Note: We need to pass the members of the applicative explicitly because Go does not
|
||||||
|
// support higher kinded types or template methods on structs or interfaces.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - HKTA = HKT<A> (e.g., Option[A], Either[E, A])
|
||||||
|
// - HKTRA = HKT<[]A> (e.g., Option[[]A], Either[E, []A])
|
||||||
|
// - HKTFRA = HKT<func(A)[]A> (e.g., Option[func(A)[]A])
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/option"
|
||||||
|
//
|
||||||
|
// opts := []option.Option[int]{
|
||||||
|
// option.Some(1),
|
||||||
|
// option.Some(2),
|
||||||
|
// option.Some(3),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// seq := array.Sequence(
|
||||||
|
// option.Of[[]int],
|
||||||
|
// option.MonadMap[[]int, func(int) []int],
|
||||||
|
// option.MonadAp[[]int, int],
|
||||||
|
// )
|
||||||
|
// result := seq(opts) // Some([1, 2, 3])
|
||||||
|
func Sequence[A, HKTA, HKTRA, HKTFRA any](
|
||||||
|
_of func([]A) HKTRA,
|
||||||
|
_map func(HKTRA, func([]A) func(A) []A) HKTFRA,
|
||||||
|
_ap func(HKTFRA, HKTA) HKTRA,
|
||||||
|
) func([]HKTA) HKTRA {
|
||||||
|
ca := F.Curry2(Append[A])
|
||||||
|
empty := _of(Empty[A]())
|
||||||
|
return Reduce(func(fas HKTRA, fa HKTA) HKTRA {
|
||||||
|
return _ap(_map(fas, ca), fa)
|
||||||
|
}, empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayOption returns a function to convert a sequence of options into an option of a sequence.
|
||||||
|
// If all options are Some, returns Some containing an array of all values.
|
||||||
|
// If any option is None, returns None.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// opts := []option.Option[int]{
|
||||||
|
// option.Some(1),
|
||||||
|
// option.Some(2),
|
||||||
|
// option.Some(3),
|
||||||
|
// }
|
||||||
|
// result := array.ArrayOption[int]()(opts) // Some([1, 2, 3])
|
||||||
|
//
|
||||||
|
// opts2 := []option.Option[int]{
|
||||||
|
// option.Some(1),
|
||||||
|
// option.None[int](),
|
||||||
|
// option.Some(3),
|
||||||
|
// }
|
||||||
|
// result2 := array.ArrayOption[int]()(opts2) // None
|
||||||
|
func ArrayOption[A any]() func([]O.Option[A]) O.Option[[]A] {
|
||||||
|
return Sequence(
|
||||||
|
O.Of[[]A],
|
||||||
|
O.MonadMap[[]A, func(A) []A],
|
||||||
|
O.MonadAp[[]A, A],
|
||||||
|
)
|
||||||
|
}
|
||||||
31
v2/array/sequence_test.go
Normal file
31
v2/array/sequence_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSequenceOption(t *testing.T) {
|
||||||
|
seq := ArrayOption[int]()
|
||||||
|
|
||||||
|
assert.Equal(t, O.Of([]int{1, 3}), seq([]O.Option[int]{O.Of(1), O.Of(3)}))
|
||||||
|
assert.Equal(t, O.None[[]int](), seq([]O.Option[int]{O.Of(1), O.None[int]()}))
|
||||||
|
}
|
||||||
92
v2/array/sort.go
Normal file
92
v2/array/sort.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
O "github.com/IBM/fp-go/v2/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort implements a stable sort on the array given the provided ordering.
|
||||||
|
// The sort is stable, meaning that elements that compare equal retain their original order.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/ord"
|
||||||
|
//
|
||||||
|
// numbers := []int{3, 1, 4, 1, 5, 9, 2, 6}
|
||||||
|
// sorted := array.Sort(ord.FromStrictCompare[int]())(numbers)
|
||||||
|
// // Result: [1, 1, 2, 3, 4, 5, 6, 9]
|
||||||
|
func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
||||||
|
return G.Sort[[]T](ord)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortByKey implements a stable sort on the array given the provided ordering on an extracted key.
|
||||||
|
// This is useful when you want to sort complex types by a specific field.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/ord"
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// people := []Person{
|
||||||
|
// {"Alice", 30},
|
||||||
|
// {"Bob", 25},
|
||||||
|
// {"Charlie", 35},
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sortByAge := array.SortByKey(
|
||||||
|
// ord.FromStrictCompare[int](),
|
||||||
|
// func(p Person) int { return p.Age },
|
||||||
|
// )
|
||||||
|
// sorted := sortByAge(people)
|
||||||
|
// // Result: [{"Bob", 25}, {"Alice", 30}, {"Charlie", 35}]
|
||||||
|
func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T {
|
||||||
|
return G.SortByKey[[]T](ord, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortBy implements a stable sort on the array using multiple ordering criteria.
|
||||||
|
// The orderings are applied in sequence: if two elements are equal according to the first
|
||||||
|
// ordering, the second ordering is used, and so on.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/ord"
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// LastName string
|
||||||
|
// FirstName string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// people := []Person{
|
||||||
|
// {"Smith", "John"},
|
||||||
|
// {"Smith", "Alice"},
|
||||||
|
// {"Jones", "Bob"},
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sortByName := array.SortBy([]ord.Ord[Person]{
|
||||||
|
// ord.Contramap(func(p Person) string { return p.LastName })(ord.FromStrictCompare[string]()),
|
||||||
|
// ord.Contramap(func(p Person) string { return p.FirstName })(ord.FromStrictCompare[string]()),
|
||||||
|
// })
|
||||||
|
// sorted := sortByName(people)
|
||||||
|
// // Result: [{"Jones", "Bob"}, {"Smith", "Alice"}, {"Smith", "John"}]
|
||||||
|
func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T {
|
||||||
|
return G.SortBy[[]T, []O.Ord[T]](ord)
|
||||||
|
}
|
||||||
36
v2/array/sort_test.go
Normal file
36
v2/array/sort_test.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/v2/ord"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSort(t *testing.T) {
|
||||||
|
|
||||||
|
ordInt := O.FromStrictCompare[int]()
|
||||||
|
|
||||||
|
input := []int{2, 1, 3}
|
||||||
|
|
||||||
|
res := Sort(ordInt)(input)
|
||||||
|
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, res)
|
||||||
|
assert.Equal(t, []int{2, 1, 3}, input)
|
||||||
|
|
||||||
|
}
|
||||||
74
v2/array/testing/laws.go
Normal file
74
v2/array/testing/laws.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// 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 testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
RA "github.com/IBM/fp-go/v2/array"
|
||||||
|
EQ "github.com/IBM/fp-go/v2/eq"
|
||||||
|
L "github.com/IBM/fp-go/v2/internal/monad/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssertLaws asserts the apply monad laws for the array monad
|
||||||
|
func AssertLaws[A, B, C any](t *testing.T,
|
||||||
|
eqa EQ.Eq[A],
|
||||||
|
eqb EQ.Eq[B],
|
||||||
|
eqc EQ.Eq[C],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(a A) bool {
|
||||||
|
|
||||||
|
return L.AssertLaws(t,
|
||||||
|
RA.Eq(eqa),
|
||||||
|
RA.Eq(eqb),
|
||||||
|
RA.Eq(eqc),
|
||||||
|
|
||||||
|
RA.Of[A],
|
||||||
|
RA.Of[B],
|
||||||
|
RA.Of[C],
|
||||||
|
|
||||||
|
RA.Of[func(A) A],
|
||||||
|
RA.Of[func(A) B],
|
||||||
|
RA.Of[func(B) C],
|
||||||
|
RA.Of[func(func(A) B) B],
|
||||||
|
|
||||||
|
RA.MonadMap[A, A],
|
||||||
|
RA.MonadMap[A, B],
|
||||||
|
RA.MonadMap[A, C],
|
||||||
|
RA.MonadMap[B, C],
|
||||||
|
|
||||||
|
RA.MonadMap[func(B) C, func(func(A) B) func(A) C],
|
||||||
|
|
||||||
|
RA.MonadChain[A, A],
|
||||||
|
RA.MonadChain[A, B],
|
||||||
|
RA.MonadChain[A, C],
|
||||||
|
RA.MonadChain[B, C],
|
||||||
|
|
||||||
|
RA.MonadAp[A, A],
|
||||||
|
RA.MonadAp[B, A],
|
||||||
|
RA.MonadAp[C, B],
|
||||||
|
RA.MonadAp[C, A],
|
||||||
|
|
||||||
|
RA.MonadAp[B, func(A) B],
|
||||||
|
RA.MonadAp[func(A) C, func(A) B],
|
||||||
|
|
||||||
|
ab,
|
||||||
|
bc,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
47
v2/array/testing/laws_test.go
Normal file
47
v2/array/testing/laws_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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 testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
EQ "github.com/IBM/fp-go/v2/eq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMonadLaws(t *testing.T) {
|
||||||
|
// some comparison
|
||||||
|
eqa := EQ.FromStrictEquals[bool]()
|
||||||
|
eqb := EQ.FromStrictEquals[int]()
|
||||||
|
eqc := EQ.FromStrictEquals[string]()
|
||||||
|
|
||||||
|
ab := func(a bool) int {
|
||||||
|
if a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bc := func(b int) string {
|
||||||
|
return fmt.Sprintf("value %d", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
laws := AssertLaws(t, eqa, eqb, eqc, ab, bc)
|
||||||
|
|
||||||
|
assert.True(t, laws(true))
|
||||||
|
assert.True(t, laws(false))
|
||||||
|
}
|
||||||
78
v2/array/traverse.go
Normal file
78
v2/array/traverse.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/internal/array"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Traverse maps each element of an array to an effect (HKT), then collects the results
|
||||||
|
// into an effect of an array. This is like a combination of Map and Sequence.
|
||||||
|
//
|
||||||
|
// Unlike Sequence which works with []HKT<A> -> HKT<[]A>, Traverse works with
|
||||||
|
// []A -> (A -> HKT<B>) -> HKT<[]B>, allowing you to transform elements while sequencing effects.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - HKTB = HKT<B> (e.g., Option[B], Either[E, B])
|
||||||
|
// - HKTAB = HKT<func(B)[]B> (intermediate type for applicative)
|
||||||
|
// - HKTRB = HKT<[]B> (e.g., Option[[]B], Either[E, []B])
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/option"
|
||||||
|
// "strconv"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Parse strings to ints, returning None if any parse fails
|
||||||
|
// parseAll := array.Traverse(
|
||||||
|
// option.Of[[]int],
|
||||||
|
// option.Map[[]int, func(int) []int],
|
||||||
|
// option.Ap[[]int, int],
|
||||||
|
// func(s string) option.Option[int] {
|
||||||
|
// if n, err := strconv.Atoi(s); err == nil {
|
||||||
|
// return option.Some(n)
|
||||||
|
// }
|
||||||
|
// return option.None[int]()
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := parseAll([]string{"1", "2", "3"}) // Some([1, 2, 3])
|
||||||
|
// result2 := parseAll([]string{"1", "x", "3"}) // None
|
||||||
|
func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||||
|
fof func([]B) HKTRB,
|
||||||
|
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
||||||
|
fap func(HKTB) func(HKTAB) HKTRB,
|
||||||
|
|
||||||
|
f func(A) HKTB) func([]A) HKTRB {
|
||||||
|
return array.Traverse[[]A](fof, fmap, fap, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadTraverse is the monadic version of Traverse that takes the array as a parameter.
|
||||||
|
// It maps each element of an array to an effect (HKT), then collects the results
|
||||||
|
// into an effect of an array.
|
||||||
|
//
|
||||||
|
// This is useful when you want to apply the traverse operation directly without currying.
|
||||||
|
func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||||
|
fof func([]B) HKTRB,
|
||||||
|
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
||||||
|
fap func(HKTB) func(HKTAB) HKTRB,
|
||||||
|
|
||||||
|
ta []A,
|
||||||
|
f func(A) HKTB) HKTRB {
|
||||||
|
|
||||||
|
return array.MonadTraverse(fof, fmap, fap, ta, f)
|
||||||
|
}
|
||||||
43
v2/array/traverse_test.go
Normal file
43
v2/array/traverse_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ArrayType = []int
|
||||||
|
|
||||||
|
func TestTraverse(t *testing.T) {
|
||||||
|
|
||||||
|
traverse := Traverse(
|
||||||
|
O.Of[ArrayType],
|
||||||
|
O.Map[ArrayType, func(int) ArrayType],
|
||||||
|
O.Ap[ArrayType, int],
|
||||||
|
|
||||||
|
func(n int) O.Option[int] {
|
||||||
|
if n%2 == 0 {
|
||||||
|
return O.None[int]()
|
||||||
|
}
|
||||||
|
return O.Of(n)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, O.None[[]int](), traverse(ArrayType{1, 2}))
|
||||||
|
assert.Equal(t, O.Of(ArrayType{1, 3}), traverse(ArrayType{1, 3}))
|
||||||
|
}
|
||||||
47
v2/array/uniq.go
Normal file
47
v2/array/uniq.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StrictUniq converts an array of arbitrary items into an array of unique items
|
||||||
|
// where uniqueness is determined by the built-in equality constraint (comparable).
|
||||||
|
// The first occurrence of each unique value is kept, subsequent duplicates are removed.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// numbers := []int{1, 2, 2, 3, 3, 3, 4}
|
||||||
|
// unique := array.StrictUniq(numbers) // [1, 2, 3, 4]
|
||||||
|
//
|
||||||
|
// strings := []string{"a", "b", "a", "c", "b"}
|
||||||
|
// unique2 := array.StrictUniq(strings) // ["a", "b", "c"]
|
||||||
|
func StrictUniq[A comparable](as []A) []A {
|
||||||
|
return G.StrictUniq[[]A](as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uniq converts an array of arbitrary items into an array of unique items
|
||||||
|
// where uniqueness is determined based on a key extractor function.
|
||||||
|
// The first occurrence of each unique key is kept, subsequent duplicates are removed.
|
||||||
|
//
|
||||||
|
// This is useful for removing duplicates from arrays of complex types based on a specific field.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// people := []Person{
|
||||||
|
// {"Alice", 30},
|
||||||
|
// {"Bob", 25},
|
||||||
|
// {"Alice", 35}, // duplicate name
|
||||||
|
// {"Charlie", 30},
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// uniqueByName := array.Uniq(func(p Person) string { return p.Name })
|
||||||
|
// result := uniqueByName(people)
|
||||||
|
// // Result: [{"Alice", 30}, {"Bob", 25}, {"Charlie", 30}]
|
||||||
|
func Uniq[A any, K comparable](f func(A) K) func(as []A) []A {
|
||||||
|
return G.Uniq[[]A](f)
|
||||||
|
}
|
||||||
14
v2/array/uniq_test.go
Normal file
14
v2/array/uniq_test.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUniq(t *testing.T) {
|
||||||
|
data := From(1, 2, 3, 2, 4, 1)
|
||||||
|
|
||||||
|
uniq := StrictUniq(data)
|
||||||
|
assert.Equal(t, From(1, 2, 3, 4), uniq)
|
||||||
|
}
|
||||||
77
v2/array/zip.go
Normal file
77
v2/array/zip.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/array/generic"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZipWith applies a function to pairs of elements at the same index in two arrays,
|
||||||
|
// collecting the results in a new array. If one input array is shorter, excess elements
|
||||||
|
// of the longer array are discarded.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// names := []string{"Alice", "Bob", "Charlie"}
|
||||||
|
// ages := []int{30, 25, 35}
|
||||||
|
//
|
||||||
|
// result := array.ZipWith(names, ages, func(name string, age int) string {
|
||||||
|
// return fmt.Sprintf("%s is %d years old", name, age)
|
||||||
|
// })
|
||||||
|
// // Result: ["Alice is 30 years old", "Bob is 25 years old", "Charlie is 35 years old"]
|
||||||
|
func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C {
|
||||||
|
return G.ZipWith[[]A, []B, []C, FCT](fa, fb, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zip takes two arrays and returns an array of corresponding pairs (tuples).
|
||||||
|
// If one input array is shorter, excess elements of the longer array are discarded.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// names := []string{"Alice", "Bob", "Charlie"}
|
||||||
|
// ages := []int{30, 25, 35}
|
||||||
|
//
|
||||||
|
// pairs := array.Zip(ages)(names)
|
||||||
|
// // Result: [(Alice, 30), (Bob, 25), (Charlie, 35)]
|
||||||
|
//
|
||||||
|
// // With different lengths
|
||||||
|
// pairs2 := array.Zip([]int{1, 2})([]string{"a", "b", "c"})
|
||||||
|
// // Result: [(a, 1), (b, 2)]
|
||||||
|
func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] {
|
||||||
|
return G.Zip[[]A, []B, []T.Tuple2[A, B]](fb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip is the reverse of Zip. It takes an array of pairs (tuples) and returns
|
||||||
|
// two corresponding arrays, one containing all first elements and one containing all second elements.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
//
|
||||||
|
// pairs := []tuple.Tuple2[string, int]{
|
||||||
|
// tuple.MakeTuple2("Alice", 30),
|
||||||
|
// tuple.MakeTuple2("Bob", 25),
|
||||||
|
// tuple.MakeTuple2("Charlie", 35),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := array.Unzip(pairs)
|
||||||
|
// // Result: (["Alice", "Bob", "Charlie"], [30, 25, 35])
|
||||||
|
// names := result.Head // ["Alice", "Bob", "Charlie"]
|
||||||
|
// ages := result.Tail // [30, 25, 35]
|
||||||
|
func Unzip[A, B any](cs []T.Tuple2[A, B]) T.Tuple2[[]A, []B] {
|
||||||
|
return G.Unzip[[]A, []B, []T.Tuple2[A, B]](cs)
|
||||||
|
}
|
||||||
56
v2/array/zip_test.go
Normal file
56
v2/array/zip_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestZipWith(t *testing.T) {
|
||||||
|
left := From(1, 2, 3)
|
||||||
|
right := From("a", "b", "c", "d")
|
||||||
|
|
||||||
|
res := ZipWith(left, right, func(l int, r string) string {
|
||||||
|
return fmt.Sprintf("%s%d", r, l)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, From("a1", "b2", "c3"), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZip(t *testing.T) {
|
||||||
|
left := From(1, 2, 3)
|
||||||
|
right := From("a", "b", "c", "d")
|
||||||
|
|
||||||
|
res := Zip[string](left)(right)
|
||||||
|
|
||||||
|
assert.Equal(t, From(T.MakeTuple2("a", 1), T.MakeTuple2("b", 2), T.MakeTuple2("c", 3)), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnzip(t *testing.T) {
|
||||||
|
left := From(1, 2, 3)
|
||||||
|
right := From("a", "b", "c")
|
||||||
|
|
||||||
|
zipped := Zip[string](left)(right)
|
||||||
|
|
||||||
|
unzipped := Unzip(zipped)
|
||||||
|
|
||||||
|
assert.Equal(t, right, unzipped.F1)
|
||||||
|
assert.Equal(t, left, unzipped.F2)
|
||||||
|
}
|
||||||
109
v2/assert/assert_test.go
Normal file
109
v2/assert/assert_test.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// 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 assert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
EQ "github.com/IBM/fp-go/v2/eq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errTest = fmt.Errorf("test failure")
|
||||||
|
|
||||||
|
// Eq is the equal predicate checking if objects are equal
|
||||||
|
Eq = EQ.FromEquals(assert.ObjectsAreEqual)
|
||||||
|
)
|
||||||
|
|
||||||
|
func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return func(actual T) E.Either[error, T] {
|
||||||
|
ok := wrapped(t, expected, actual)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotEqual tests if the expected and the actual values are not equal
|
||||||
|
func NotEqual[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return wrap1(assert.NotEqual, t, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests if the expected and the actual values are equal
|
||||||
|
func Equal[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
||||||
|
return wrap1(assert.Equal, t, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length tests if an array has the expected length
|
||||||
|
func Length[T any](t *testing.T, expected int) func(actual []T) E.Either[error, []T] {
|
||||||
|
return func(actual []T) E.Either[error, []T] {
|
||||||
|
ok := assert.Len(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[[]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoError validates that there is no error
|
||||||
|
func NoError[T any](t *testing.T) func(actual E.Either[error, T]) E.Either[error, T] {
|
||||||
|
return func(actual E.Either[error, T]) E.Either[error, T] {
|
||||||
|
return E.MonadFold(actual, func(e error) E.Either[error, T] {
|
||||||
|
assert.NoError(t, e)
|
||||||
|
return E.Left[T](e)
|
||||||
|
}, func(value T) E.Either[error, T] {
|
||||||
|
assert.NoError(t, nil)
|
||||||
|
return E.Right[error](value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayContains tests if a value is contained in an array
|
||||||
|
func ArrayContains[T any](t *testing.T, expected T) func(actual []T) E.Either[error, []T] {
|
||||||
|
return func(actual []T) E.Either[error, []T] {
|
||||||
|
ok := assert.Contains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[[]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsKey tests if a key is contained in a map
|
||||||
|
func ContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
ok := assert.Contains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[map[K]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotContainsKey tests if a key is not contained in a map
|
||||||
|
func NotContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
return func(actual map[K]T) E.Either[error, map[K]T] {
|
||||||
|
ok := assert.NotContains(t, actual, expected)
|
||||||
|
if ok {
|
||||||
|
return E.Of[error](actual)
|
||||||
|
}
|
||||||
|
return E.Left[map[K]T](errTest)
|
||||||
|
}
|
||||||
|
}
|
||||||
59
v2/boolean/boolean.go
Normal file
59
v2/boolean/boolean.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// 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 boolean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/eq"
|
||||||
|
"github.com/IBM/fp-go/v2/monoid"
|
||||||
|
"github.com/IBM/fp-go/v2/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// MonoidAny is the boolean [monoid.Monoid] under disjunction
|
||||||
|
MonoidAny = monoid.MakeMonoid(
|
||||||
|
func(l, r bool) bool {
|
||||||
|
return l || r
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
|
// MonoidAll is the boolean [monoid.Monoid] under conjuction
|
||||||
|
MonoidAll = monoid.MakeMonoid(
|
||||||
|
func(l, r bool) bool {
|
||||||
|
return l && r
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Eq is the equals predicate for boolean
|
||||||
|
Eq = eq.FromStrictEquals[bool]()
|
||||||
|
|
||||||
|
// Ord is the strict ordering for boolean
|
||||||
|
Ord = ord.MakeOrd(func(l, r bool) int {
|
||||||
|
if l {
|
||||||
|
if r {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
if r {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}, func(l, r bool) bool {
|
||||||
|
return l == r
|
||||||
|
})
|
||||||
|
)
|
||||||
246
v2/boolean/boolean_test.go
Normal file
246
v2/boolean/boolean_test.go
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
// 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 boolean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMonoidAny(t *testing.T) {
|
||||||
|
t.Run("identity element is false", func(t *testing.T) {
|
||||||
|
assert.Equal(t, false, MonoidAny.Empty())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false OR false = false", func(t *testing.T) {
|
||||||
|
result := MonoidAny.Concat(false, false)
|
||||||
|
assert.Equal(t, false, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false OR true = true", func(t *testing.T) {
|
||||||
|
result := MonoidAny.Concat(false, true)
|
||||||
|
assert.Equal(t, true, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true OR false = true", func(t *testing.T) {
|
||||||
|
result := MonoidAny.Concat(true, false)
|
||||||
|
assert.Equal(t, true, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true OR true = true", func(t *testing.T) {
|
||||||
|
result := MonoidAny.Concat(true, true)
|
||||||
|
assert.Equal(t, true, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("left identity: empty OR x = x", func(t *testing.T) {
|
||||||
|
assert.Equal(t, true, MonoidAny.Concat(MonoidAny.Empty(), true))
|
||||||
|
assert.Equal(t, false, MonoidAny.Concat(MonoidAny.Empty(), false))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("right identity: x OR empty = x", func(t *testing.T) {
|
||||||
|
assert.Equal(t, true, MonoidAny.Concat(true, MonoidAny.Empty()))
|
||||||
|
assert.Equal(t, false, MonoidAny.Concat(false, MonoidAny.Empty()))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("associativity: (a OR b) OR c = a OR (b OR c)", func(t *testing.T) {
|
||||||
|
a, b, c := true, false, true
|
||||||
|
left := MonoidAny.Concat(MonoidAny.Concat(a, b), c)
|
||||||
|
right := MonoidAny.Concat(a, MonoidAny.Concat(b, c))
|
||||||
|
assert.Equal(t, left, right)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonoidAll(t *testing.T) {
|
||||||
|
t.Run("identity element is true", func(t *testing.T) {
|
||||||
|
assert.Equal(t, true, MonoidAll.Empty())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false AND false = false", func(t *testing.T) {
|
||||||
|
result := MonoidAll.Concat(false, false)
|
||||||
|
assert.Equal(t, false, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false AND true = false", func(t *testing.T) {
|
||||||
|
result := MonoidAll.Concat(false, true)
|
||||||
|
assert.Equal(t, false, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true AND false = false", func(t *testing.T) {
|
||||||
|
result := MonoidAll.Concat(true, false)
|
||||||
|
assert.Equal(t, false, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true AND true = true", func(t *testing.T) {
|
||||||
|
result := MonoidAll.Concat(true, true)
|
||||||
|
assert.Equal(t, true, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("left identity: empty AND x = x", func(t *testing.T) {
|
||||||
|
assert.Equal(t, true, MonoidAll.Concat(MonoidAll.Empty(), true))
|
||||||
|
assert.Equal(t, false, MonoidAll.Concat(MonoidAll.Empty(), false))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("right identity: x AND empty = x", func(t *testing.T) {
|
||||||
|
assert.Equal(t, true, MonoidAll.Concat(true, MonoidAll.Empty()))
|
||||||
|
assert.Equal(t, false, MonoidAll.Concat(false, MonoidAll.Empty()))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("associativity: (a AND b) AND c = a AND (b AND c)", func(t *testing.T) {
|
||||||
|
a, b, c := true, false, true
|
||||||
|
left := MonoidAll.Concat(MonoidAll.Concat(a, b), c)
|
||||||
|
right := MonoidAll.Concat(a, MonoidAll.Concat(b, c))
|
||||||
|
assert.Equal(t, left, right)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEq(t *testing.T) {
|
||||||
|
t.Run("true equals true", func(t *testing.T) {
|
||||||
|
assert.True(t, Eq.Equals(true, true))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false equals false", func(t *testing.T) {
|
||||||
|
assert.True(t, Eq.Equals(false, false))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true not equals false", func(t *testing.T) {
|
||||||
|
assert.False(t, Eq.Equals(true, false))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false not equals true", func(t *testing.T) {
|
||||||
|
assert.False(t, Eq.Equals(false, true))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("reflexivity: x equals x", func(t *testing.T) {
|
||||||
|
assert.True(t, Eq.Equals(true, true))
|
||||||
|
assert.True(t, Eq.Equals(false, false))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("symmetry: if x equals y then y equals x", func(t *testing.T) {
|
||||||
|
assert.Equal(t, Eq.Equals(true, false), Eq.Equals(false, true))
|
||||||
|
assert.Equal(t, Eq.Equals(true, true), Eq.Equals(true, true))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("transitivity: if x equals y and y equals z then x equals z", func(t *testing.T) {
|
||||||
|
x, y, z := true, true, true
|
||||||
|
if Eq.Equals(x, y) && Eq.Equals(y, z) {
|
||||||
|
assert.True(t, Eq.Equals(x, z))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrd(t *testing.T) {
|
||||||
|
t.Run("false < true", func(t *testing.T) {
|
||||||
|
result := Ord.Compare(false, true)
|
||||||
|
assert.Equal(t, -1, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true > false", func(t *testing.T) {
|
||||||
|
result := Ord.Compare(true, false)
|
||||||
|
assert.Equal(t, 1, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("true == true", func(t *testing.T) {
|
||||||
|
result := Ord.Compare(true, true)
|
||||||
|
assert.Equal(t, 0, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("false == false", func(t *testing.T) {
|
||||||
|
result := Ord.Compare(false, false)
|
||||||
|
assert.Equal(t, 0, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Equals method works", func(t *testing.T) {
|
||||||
|
assert.True(t, Ord.Equals(true, true))
|
||||||
|
assert.True(t, Ord.Equals(false, false))
|
||||||
|
assert.False(t, Ord.Equals(true, false))
|
||||||
|
assert.False(t, Ord.Equals(false, true))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("reflexivity: x <= x", func(t *testing.T) {
|
||||||
|
assert.True(t, Ord.Compare(true, true) == 0)
|
||||||
|
assert.True(t, Ord.Compare(false, false) == 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("antisymmetry: if x <= y and y <= x then x == y", func(t *testing.T) {
|
||||||
|
x, y := true, true
|
||||||
|
if Ord.Compare(x, y) <= 0 && Ord.Compare(y, x) <= 0 {
|
||||||
|
assert.Equal(t, 0, Ord.Compare(x, y))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("transitivity: if x <= y and y <= z then x <= z", func(t *testing.T) {
|
||||||
|
x, y, z := false, true, true
|
||||||
|
if Ord.Compare(x, y) <= 0 && Ord.Compare(y, z) <= 0 {
|
||||||
|
assert.True(t, Ord.Compare(x, z) <= 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("totality: x <= y or y <= x", func(t *testing.T) {
|
||||||
|
assert.True(t, Ord.Compare(true, false) >= 0 || Ord.Compare(false, true) >= 0)
|
||||||
|
assert.True(t, Ord.Compare(false, true) <= 0 || Ord.Compare(true, false) <= 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example tests that also serve as documentation
|
||||||
|
func ExampleMonoidAny() {
|
||||||
|
// Combine booleans with OR
|
||||||
|
result := MonoidAny.Concat(false, true)
|
||||||
|
println(result) // true
|
||||||
|
|
||||||
|
// Identity element
|
||||||
|
identity := MonoidAny.Empty()
|
||||||
|
println(identity) // false
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleMonoidAll() {
|
||||||
|
// Combine booleans with AND
|
||||||
|
result := MonoidAll.Concat(true, true)
|
||||||
|
println(result) // true
|
||||||
|
|
||||||
|
// Identity element
|
||||||
|
identity := MonoidAll.Empty()
|
||||||
|
println(identity) // true
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleEq() {
|
||||||
|
// Check equality
|
||||||
|
equal := Eq.Equals(true, true)
|
||||||
|
println(equal) // true
|
||||||
|
|
||||||
|
notEqual := Eq.Equals(true, false)
|
||||||
|
println(notEqual) // false
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleOrd() {
|
||||||
|
// Compare booleans (false < true)
|
||||||
|
cmp := Ord.Compare(false, true)
|
||||||
|
println(cmp) // -1
|
||||||
|
|
||||||
|
cmp2 := Ord.Compare(true, false)
|
||||||
|
println(cmp2) // 1
|
||||||
|
|
||||||
|
cmp3 := Ord.Compare(true, true)
|
||||||
|
println(cmp3) // 0
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
138
v2/boolean/doc.go
Normal file
138
v2/boolean/doc.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// 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 boolean provides functional programming utilities for working with boolean values.
|
||||||
|
//
|
||||||
|
// This package offers algebraic structures (Monoid, Eq, Ord) for boolean values,
|
||||||
|
// enabling functional composition and reasoning about boolean operations.
|
||||||
|
//
|
||||||
|
// # Monoids
|
||||||
|
//
|
||||||
|
// The package provides two monoid instances for booleans:
|
||||||
|
//
|
||||||
|
// - MonoidAny: Combines booleans using logical OR (disjunction), with false as identity
|
||||||
|
// - MonoidAll: Combines booleans using logical AND (conjunction), with true as identity
|
||||||
|
//
|
||||||
|
// # MonoidAny - Logical OR
|
||||||
|
//
|
||||||
|
// MonoidAny implements the boolean monoid under disjunction (OR operation).
|
||||||
|
// The identity element is false, meaning false OR x = x for any boolean x.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/boolean"
|
||||||
|
//
|
||||||
|
// // Combine multiple booleans with OR
|
||||||
|
// result := boolean.MonoidAny.Concat(false, true) // true
|
||||||
|
// result2 := boolean.MonoidAny.Concat(false, false) // false
|
||||||
|
//
|
||||||
|
// // Identity element
|
||||||
|
// identity := boolean.MonoidAny.Empty() // false
|
||||||
|
//
|
||||||
|
// // Check if any value in a collection is true
|
||||||
|
// import "github.com/IBM/fp-go/v2/array"
|
||||||
|
// values := []bool{false, false, true, false}
|
||||||
|
// anyTrue := array.Fold(boolean.MonoidAny)(values) // true
|
||||||
|
//
|
||||||
|
// # MonoidAll - Logical AND
|
||||||
|
//
|
||||||
|
// MonoidAll implements the boolean monoid under conjunction (AND operation).
|
||||||
|
// The identity element is true, meaning true AND x = x for any boolean x.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/boolean"
|
||||||
|
//
|
||||||
|
// // Combine multiple booleans with AND
|
||||||
|
// result := boolean.MonoidAll.Concat(true, true) // true
|
||||||
|
// result2 := boolean.MonoidAll.Concat(true, false) // false
|
||||||
|
//
|
||||||
|
// // Identity element
|
||||||
|
// identity := boolean.MonoidAll.Empty() // true
|
||||||
|
//
|
||||||
|
// // Check if all values in a collection are true
|
||||||
|
// import "github.com/IBM/fp-go/v2/array"
|
||||||
|
// values := []bool{true, true, true}
|
||||||
|
// allTrue := array.Fold(boolean.MonoidAll)(values) // true
|
||||||
|
//
|
||||||
|
// # Equality
|
||||||
|
//
|
||||||
|
// The Eq instance provides structural equality for booleans:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/boolean"
|
||||||
|
//
|
||||||
|
// equal := boolean.Eq.Equals(true, true) // true
|
||||||
|
// equal2 := boolean.Eq.Equals(true, false) // false
|
||||||
|
//
|
||||||
|
// # Ordering
|
||||||
|
//
|
||||||
|
// The Ord instance provides a total ordering for booleans where false < true:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/boolean"
|
||||||
|
//
|
||||||
|
// cmp := boolean.Ord.Compare(false, true) // -1 (false < true)
|
||||||
|
// cmp2 := boolean.Ord.Compare(true, false) // +1 (true > false)
|
||||||
|
// cmp3 := boolean.Ord.Compare(true, true) // 0 (equal)
|
||||||
|
//
|
||||||
|
// # Use Cases
|
||||||
|
//
|
||||||
|
// The boolean package is particularly useful for:
|
||||||
|
//
|
||||||
|
// - Combining multiple boolean conditions functionally
|
||||||
|
// - Implementing validation logic that accumulates results
|
||||||
|
// - Working with predicates in a composable way
|
||||||
|
// - Folding collections of boolean values
|
||||||
|
//
|
||||||
|
// Example - Validation:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/array"
|
||||||
|
// "github.com/IBM/fp-go/v2/boolean"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// type User struct {
|
||||||
|
// Name string
|
||||||
|
// Email string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Define validation predicates
|
||||||
|
// validations := []func(User) bool{
|
||||||
|
// func(u User) bool { return len(u.Name) > 0 },
|
||||||
|
// func(u User) bool { return len(u.Email) > 0 },
|
||||||
|
// func(u User) bool { return u.Age >= 18 },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check if user passes all validations
|
||||||
|
// user := User{"Alice", "alice@example.com", 25}
|
||||||
|
// results := array.Map(func(v func(User) bool) bool {
|
||||||
|
// return v(user)
|
||||||
|
// })(validations)
|
||||||
|
// allValid := array.Fold(boolean.MonoidAll)(results) // true
|
||||||
|
//
|
||||||
|
// Example - Any Match:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/array"
|
||||||
|
// "github.com/IBM/fp-go/v2/boolean"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Check if any number is even
|
||||||
|
// numbers := []int{1, 3, 5, 7, 8, 9}
|
||||||
|
// checks := array.Map(func(n int) bool {
|
||||||
|
// return n%2 == 0
|
||||||
|
// })(numbers)
|
||||||
|
// hasEven := array.Fold(boolean.MonoidAny)(checks) // true
|
||||||
|
package boolean
|
||||||
22
v2/boolean/types.go
Normal file
22
v2/boolean/types.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// 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 boolean
|
||||||
|
|
||||||
|
import "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Monoid = monoid.Monoid[bool]
|
||||||
|
)
|
||||||
62
v2/bounded/bounded.go
Normal file
62
v2/bounded/bounded.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// 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 bounded
|
||||||
|
|
||||||
|
import "github.com/IBM/fp-go/v2/ord"
|
||||||
|
|
||||||
|
type Bounded[T any] interface {
|
||||||
|
ord.Ord[T]
|
||||||
|
Top() T
|
||||||
|
Bottom() T
|
||||||
|
}
|
||||||
|
|
||||||
|
type bounded[T any] struct {
|
||||||
|
c func(x, y T) int
|
||||||
|
e func(x, y T) bool
|
||||||
|
t T
|
||||||
|
b T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bounded[T]) Equals(x, y T) bool {
|
||||||
|
return b.e(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bounded[T]) Compare(x, y T) int {
|
||||||
|
return b.c(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bounded[T]) Top() T {
|
||||||
|
return b.t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bounded[T]) Bottom() T {
|
||||||
|
return b.b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBounded creates an instance of a bounded type
|
||||||
|
func MakeBounded[T any](o ord.Ord[T], t, b T) Bounded[T] {
|
||||||
|
return bounded[T]{c: o.Compare, e: o.Equals, t: t, b: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp returns a function that clamps against the bounds defined in the bounded type
|
||||||
|
func Clamp[T any](b Bounded[T]) func(T) T {
|
||||||
|
return ord.Clamp[T](b)(b.Bottom(), b.Top())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse reverses the ordering and swaps the bounds
|
||||||
|
func Reverse[T any](b Bounded[T]) Bounded[T] {
|
||||||
|
return MakeBounded(ord.Reverse(b), b.Bottom(), b.Top())
|
||||||
|
}
|
||||||
212
v2/bounded/bounded_test.go
Normal file
212
v2/bounded/bounded_test.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
// 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 bounded
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/ord"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeBounded(t *testing.T) {
|
||||||
|
t.Run("creates bounded instance with correct top and bottom", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
|
||||||
|
assert.Equal(t, 100, b.Top())
|
||||||
|
assert.Equal(t, 0, b.Bottom())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("preserves ordering from Ord", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
|
||||||
|
assert.Equal(t, -1, b.Compare(5, 10))
|
||||||
|
assert.Equal(t, 0, b.Compare(5, 5))
|
||||||
|
assert.Equal(t, 1, b.Compare(10, 5))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("preserves equality from Ord", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
|
||||||
|
assert.True(t, b.Equals(5, 5))
|
||||||
|
assert.False(t, b.Equals(5, 10))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClamp(t *testing.T) {
|
||||||
|
t.Run("returns value within bounds unchanged", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
clamp := Clamp(b)
|
||||||
|
|
||||||
|
assert.Equal(t, 50, clamp(50))
|
||||||
|
assert.Equal(t, 0, clamp(0))
|
||||||
|
assert.Equal(t, 100, clamp(100))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("clamps value above top to top", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
clamp := Clamp(b)
|
||||||
|
|
||||||
|
assert.Equal(t, 100, clamp(150))
|
||||||
|
assert.Equal(t, 100, clamp(200))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("clamps value below bottom to bottom", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
clamp := Clamp(b)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, clamp(-10))
|
||||||
|
assert.Equal(t, 0, clamp(-100))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("works with float64", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[float64](), 1.0, 0.0)
|
||||||
|
clamp := Clamp(b)
|
||||||
|
|
||||||
|
assert.Equal(t, 0.5, clamp(0.5))
|
||||||
|
assert.Equal(t, 1.0, clamp(1.5))
|
||||||
|
assert.Equal(t, 0.0, clamp(-0.5))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("works with strings", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[string](), "z", "a")
|
||||||
|
clamp := Clamp(b)
|
||||||
|
|
||||||
|
assert.Equal(t, "m", clamp("m"))
|
||||||
|
assert.Equal(t, "z", clamp("zzz"))
|
||||||
|
assert.Equal(t, "a", clamp("A"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverse(t *testing.T) {
|
||||||
|
t.Run("reverses the ordering", func(t *testing.T) {
|
||||||
|
original := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
reversed := Reverse(original)
|
||||||
|
|
||||||
|
// In original: 5 < 10, so Compare(5, 10) = -1
|
||||||
|
assert.Equal(t, -1, original.Compare(5, 10))
|
||||||
|
|
||||||
|
// In reversed: 5 > 10, so Compare(5, 10) = 1
|
||||||
|
assert.Equal(t, 1, reversed.Compare(5, 10))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("swaps top and bottom values", func(t *testing.T) {
|
||||||
|
original := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
reversed := Reverse(original)
|
||||||
|
|
||||||
|
// Reverse swaps the bounds
|
||||||
|
assert.Equal(t, original.Bottom(), reversed.Top())
|
||||||
|
assert.Equal(t, original.Top(), reversed.Bottom())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("double reverse returns to original ordering", func(t *testing.T) {
|
||||||
|
original := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
reversed := Reverse(original)
|
||||||
|
doubleReversed := Reverse(reversed)
|
||||||
|
|
||||||
|
assert.Equal(t, original.Compare(5, 10), doubleReversed.Compare(5, 10))
|
||||||
|
assert.Equal(t, original.Compare(10, 5), doubleReversed.Compare(10, 5))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("preserves equality", func(t *testing.T) {
|
||||||
|
original := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
reversed := Reverse(original)
|
||||||
|
|
||||||
|
assert.Equal(t, original.Equals(5, 5), reversed.Equals(5, 5))
|
||||||
|
assert.Equal(t, original.Equals(5, 10), reversed.Equals(5, 10))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoundedLaws(t *testing.T) {
|
||||||
|
t.Run("bottom is less than or equal to all values", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
|
||||||
|
testValues := []int{0, 25, 50, 75, 100}
|
||||||
|
for _, v := range testValues {
|
||||||
|
assert.True(t, b.Compare(b.Bottom(), v) <= 0,
|
||||||
|
"Bottom (%d) should be <= %d", b.Bottom(), v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("top is greater than or equal to all values", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
|
||||||
|
testValues := []int{0, 25, 50, 75, 100}
|
||||||
|
for _, v := range testValues {
|
||||||
|
assert.True(t, b.Compare(b.Top(), v) >= 0,
|
||||||
|
"Top (%d) should be >= %d", b.Top(), v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bottom is less than or equal to top", func(t *testing.T) {
|
||||||
|
b := MakeBounded(ord.FromStrictCompare[int](), 100, 0)
|
||||||
|
|
||||||
|
assert.True(t, b.Compare(b.Bottom(), b.Top()) <= 0,
|
||||||
|
"Bottom should be <= Top")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example tests
|
||||||
|
func ExampleMakeBounded() {
|
||||||
|
// Create a bounded type for percentages (0-100)
|
||||||
|
percentage := MakeBounded(
|
||||||
|
ord.FromStrictCompare[int](),
|
||||||
|
100, // top
|
||||||
|
0, // bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
println(percentage.Top()) // 100
|
||||||
|
println(percentage.Bottom()) // 0
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClamp() {
|
||||||
|
// Create bounded type for percentages
|
||||||
|
percentage := MakeBounded(
|
||||||
|
ord.FromStrictCompare[int](),
|
||||||
|
100, // top
|
||||||
|
0, // bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
clamp := Clamp(percentage)
|
||||||
|
|
||||||
|
println(clamp(50)) // 50 (within bounds)
|
||||||
|
println(clamp(150)) // 100 (clamped to top)
|
||||||
|
println(clamp(-10)) // 0 (clamped to bottom)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleReverse() {
|
||||||
|
original := MakeBounded(
|
||||||
|
ord.FromStrictCompare[int](),
|
||||||
|
100, // top
|
||||||
|
0, // bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
reversed := Reverse(original)
|
||||||
|
|
||||||
|
// Ordering is reversed
|
||||||
|
println(original.Compare(5, 10)) // -1 (5 < 10)
|
||||||
|
println(reversed.Compare(5, 10)) // 1 (5 > 10 in reversed)
|
||||||
|
|
||||||
|
// Bounds are swapped
|
||||||
|
println(reversed.Top()) // 0
|
||||||
|
println(reversed.Bottom()) // 100
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
149
v2/bounded/doc.go
Normal file
149
v2/bounded/doc.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// 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 bounded provides types and functions for working with bounded ordered types.
|
||||||
|
//
|
||||||
|
// A Bounded type extends Ord with minimum (Bottom) and maximum (Top) values,
|
||||||
|
// representing types that have well-defined lower and upper bounds.
|
||||||
|
//
|
||||||
|
// # Bounded Interface
|
||||||
|
//
|
||||||
|
// The Bounded interface combines ordering (Ord) with boundary values:
|
||||||
|
//
|
||||||
|
// type Bounded[T any] interface {
|
||||||
|
// ord.Ord[T]
|
||||||
|
// Top() T // Maximum value
|
||||||
|
// Bottom() T // Minimum value
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// # Creating Bounded Instances
|
||||||
|
//
|
||||||
|
// Use MakeBounded to create a Bounded instance from an Ord and boundary values:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/bounded"
|
||||||
|
// "github.com/IBM/fp-go/v2/ord"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Bounded integers from 0 to 100
|
||||||
|
// boundedInt := bounded.MakeBounded(
|
||||||
|
// ord.FromStrictCompare[int](),
|
||||||
|
// 100, // top
|
||||||
|
// 0, // bottom
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// top := boundedInt.Top() // 100
|
||||||
|
// bottom := boundedInt.Bottom() // 0
|
||||||
|
//
|
||||||
|
// # Clamping Values
|
||||||
|
//
|
||||||
|
// The Clamp function restricts values to stay within the bounds:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/bounded"
|
||||||
|
// "github.com/IBM/fp-go/v2/ord"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Create bounded type for percentages (0-100)
|
||||||
|
// percentage := bounded.MakeBounded(
|
||||||
|
// ord.FromStrictCompare[int](),
|
||||||
|
// 100, // top
|
||||||
|
// 0, // bottom
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// clamp := bounded.Clamp(percentage)
|
||||||
|
//
|
||||||
|
// result1 := clamp(50) // 50 (within bounds)
|
||||||
|
// result2 := clamp(150) // 100 (clamped to top)
|
||||||
|
// result3 := clamp(-10) // 0 (clamped to bottom)
|
||||||
|
//
|
||||||
|
// # Reversing Bounds
|
||||||
|
//
|
||||||
|
// The Reverse function swaps the ordering and bounds:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/bounded"
|
||||||
|
// "github.com/IBM/fp-go/v2/ord"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// original := bounded.MakeBounded(
|
||||||
|
// ord.FromStrictCompare[int](),
|
||||||
|
// 100, // top
|
||||||
|
// 0, // bottom
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// reversed := bounded.Reverse(original)
|
||||||
|
//
|
||||||
|
// // In reversed, ordering is flipped and bounds are swapped
|
||||||
|
// // Compare(10, 20) returns 1 instead of -1
|
||||||
|
// // Top() returns 0 and Bottom() returns 100
|
||||||
|
//
|
||||||
|
// # Use Cases
|
||||||
|
//
|
||||||
|
// Bounded types are useful for:
|
||||||
|
//
|
||||||
|
// - Representing ranges with well-defined limits (e.g., percentages, grades)
|
||||||
|
// - Implementing safe arithmetic that stays within bounds
|
||||||
|
// - Validating input values against constraints
|
||||||
|
// - Creating domain-specific types with natural boundaries
|
||||||
|
//
|
||||||
|
// # Example - Temperature Range
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/bounded"
|
||||||
|
// "github.com/IBM/fp-go/v2/ord"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Celsius temperature range for a thermostat
|
||||||
|
// thermostat := bounded.MakeBounded(
|
||||||
|
// ord.FromStrictCompare[float64](),
|
||||||
|
// 30.0, // max temperature
|
||||||
|
// 15.0, // min temperature
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// clampTemp := bounded.Clamp(thermostat)
|
||||||
|
//
|
||||||
|
// // User tries to set temperature
|
||||||
|
// desired := 35.0
|
||||||
|
// actual := clampTemp(desired) // 30.0 (clamped to maximum)
|
||||||
|
//
|
||||||
|
// # Example - Bounded Characters
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/bounded"
|
||||||
|
// "github.com/IBM/fp-go/v2/ord"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Lowercase letters only
|
||||||
|
// lowercase := bounded.MakeBounded(
|
||||||
|
// ord.FromStrictCompare[rune](),
|
||||||
|
// 'z', // top
|
||||||
|
// 'a', // bottom
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// clampChar := bounded.Clamp(lowercase)
|
||||||
|
//
|
||||||
|
// result1 := clampChar('m') // 'm' (within bounds)
|
||||||
|
// result2 := clampChar('A') // 'a' (clamped to bottom)
|
||||||
|
// result3 := clampChar('~') // 'z' (clamped to top)
|
||||||
|
//
|
||||||
|
// # Laws
|
||||||
|
//
|
||||||
|
// Bounded instances must satisfy the Ord laws plus:
|
||||||
|
//
|
||||||
|
// - Bottom is less than or equal to all values: Compare(Bottom(), x) <= 0
|
||||||
|
// - Top is greater than or equal to all values: Compare(Top(), x) >= 0
|
||||||
|
// - Bottom <= Top: Compare(Bottom(), Top()) <= 0
|
||||||
|
package bounded
|
||||||
28
v2/bytes/bytes.go
Normal file
28
v2/bytes/bytes.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// 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 bytes
|
||||||
|
|
||||||
|
func Empty() []byte {
|
||||||
|
return Monoid.Empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToString(a []byte) string {
|
||||||
|
return string(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Size(as []byte) int {
|
||||||
|
return len(as)
|
||||||
|
}
|
||||||
221
v2/bytes/bytes_test.go
Normal file
221
v2/bytes/bytes_test.go
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
// 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 bytes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmpty(t *testing.T) {
|
||||||
|
t.Run("returns empty byte slice", func(t *testing.T) {
|
||||||
|
result := Empty()
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
assert.Equal(t, 0, len(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("is identity for Monoid", func(t *testing.T) {
|
||||||
|
data := []byte("test")
|
||||||
|
|
||||||
|
// Left identity: empty + data = data
|
||||||
|
left := Monoid.Concat(Empty(), data)
|
||||||
|
assert.Equal(t, data, left)
|
||||||
|
|
||||||
|
// Right identity: data + empty = data
|
||||||
|
right := Monoid.Concat(data, Empty())
|
||||||
|
assert.Equal(t, data, right)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToString(t *testing.T) {
|
||||||
|
t.Run("converts byte slice to string", func(t *testing.T) {
|
||||||
|
result := ToString([]byte("hello"))
|
||||||
|
assert.Equal(t, "hello", result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles empty byte slice", func(t *testing.T) {
|
||||||
|
result := ToString([]byte{})
|
||||||
|
assert.Equal(t, "", result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles binary data", func(t *testing.T) {
|
||||||
|
data := []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f} // "Hello"
|
||||||
|
result := ToString(data)
|
||||||
|
assert.Equal(t, "Hello", result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSize(t *testing.T) {
|
||||||
|
t.Run("returns size of byte slice", func(t *testing.T) {
|
||||||
|
assert.Equal(t, 0, Size([]byte{}))
|
||||||
|
assert.Equal(t, 5, Size([]byte("hello")))
|
||||||
|
assert.Equal(t, 10, Size([]byte("0123456789")))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles binary data", func(t *testing.T) {
|
||||||
|
data := []byte{0x00, 0x01, 0x02, 0x03}
|
||||||
|
assert.Equal(t, 4, Size(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonoidConcat(t *testing.T) {
|
||||||
|
t.Run("concatenates two byte slices", func(t *testing.T) {
|
||||||
|
result := Monoid.Concat([]byte("Hello"), []byte(" World"))
|
||||||
|
assert.Equal(t, []byte("Hello World"), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles empty slices", func(t *testing.T) {
|
||||||
|
result1 := Monoid.Concat([]byte{}, []byte("test"))
|
||||||
|
assert.Equal(t, []byte("test"), result1)
|
||||||
|
|
||||||
|
result2 := Monoid.Concat([]byte("test"), []byte{})
|
||||||
|
assert.Equal(t, []byte("test"), result2)
|
||||||
|
|
||||||
|
result3 := Monoid.Concat([]byte{}, []byte{})
|
||||||
|
assert.Equal(t, []byte{}, result3)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("associativity: (a + b) + c = a + (b + c)", func(t *testing.T) {
|
||||||
|
a := []byte("a")
|
||||||
|
b := []byte("b")
|
||||||
|
c := []byte("c")
|
||||||
|
|
||||||
|
left := Monoid.Concat(Monoid.Concat(a, b), c)
|
||||||
|
right := Monoid.Concat(a, Monoid.Concat(b, c))
|
||||||
|
|
||||||
|
assert.Equal(t, left, right)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConcatAll(t *testing.T) {
|
||||||
|
t.Run("concatenates multiple byte slices", func(t *testing.T) {
|
||||||
|
result := ConcatAll(
|
||||||
|
[]byte("Hello"),
|
||||||
|
[]byte(" "),
|
||||||
|
[]byte("World"),
|
||||||
|
[]byte("!"),
|
||||||
|
)
|
||||||
|
assert.Equal(t, []byte("Hello World!"), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles empty input", func(t *testing.T) {
|
||||||
|
result := ConcatAll()
|
||||||
|
assert.Equal(t, []byte{}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles single slice", func(t *testing.T) {
|
||||||
|
result := ConcatAll([]byte("test"))
|
||||||
|
assert.Equal(t, []byte("test"), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles slices with empty elements", func(t *testing.T) {
|
||||||
|
result := ConcatAll(
|
||||||
|
[]byte("a"),
|
||||||
|
[]byte{},
|
||||||
|
[]byte("b"),
|
||||||
|
[]byte{},
|
||||||
|
[]byte("c"),
|
||||||
|
)
|
||||||
|
assert.Equal(t, []byte("abc"), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles binary data", func(t *testing.T) {
|
||||||
|
result := ConcatAll(
|
||||||
|
[]byte{0x01, 0x02},
|
||||||
|
[]byte{0x03, 0x04},
|
||||||
|
[]byte{0x05},
|
||||||
|
)
|
||||||
|
assert.Equal(t, []byte{0x01, 0x02, 0x03, 0x04, 0x05}, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrd(t *testing.T) {
|
||||||
|
t.Run("compares byte slices lexicographically", func(t *testing.T) {
|
||||||
|
// "abc" < "abd"
|
||||||
|
assert.Equal(t, -1, Ord.Compare([]byte("abc"), []byte("abd")))
|
||||||
|
|
||||||
|
// "abd" > "abc"
|
||||||
|
assert.Equal(t, 1, Ord.Compare([]byte("abd"), []byte("abc")))
|
||||||
|
|
||||||
|
// "abc" == "abc"
|
||||||
|
assert.Equal(t, 0, Ord.Compare([]byte("abc"), []byte("abc")))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles different lengths", func(t *testing.T) {
|
||||||
|
// "ab" < "abc"
|
||||||
|
assert.Equal(t, -1, Ord.Compare([]byte("ab"), []byte("abc")))
|
||||||
|
|
||||||
|
// "abc" > "ab"
|
||||||
|
assert.Equal(t, 1, Ord.Compare([]byte("abc"), []byte("ab")))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles empty slices", func(t *testing.T) {
|
||||||
|
// "" < "a"
|
||||||
|
assert.Equal(t, -1, Ord.Compare([]byte{}, []byte("a")))
|
||||||
|
|
||||||
|
// "a" > ""
|
||||||
|
assert.Equal(t, 1, Ord.Compare([]byte("a"), []byte{}))
|
||||||
|
|
||||||
|
// "" == ""
|
||||||
|
assert.Equal(t, 0, Ord.Compare([]byte{}, []byte{}))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Equals method works", func(t *testing.T) {
|
||||||
|
assert.True(t, Ord.Equals([]byte("test"), []byte("test")))
|
||||||
|
assert.False(t, Ord.Equals([]byte("test"), []byte("Test")))
|
||||||
|
assert.True(t, Ord.Equals([]byte{}, []byte{}))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("handles binary data", func(t *testing.T) {
|
||||||
|
assert.Equal(t, -1, Ord.Compare([]byte{0x01}, []byte{0x02}))
|
||||||
|
assert.Equal(t, 1, Ord.Compare([]byte{0x02}, []byte{0x01}))
|
||||||
|
assert.Equal(t, 0, Ord.Compare([]byte{0x01, 0x02}, []byte{0x01, 0x02}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example tests
|
||||||
|
func ExampleEmpty() {
|
||||||
|
empty := Empty()
|
||||||
|
println(len(empty)) // 0
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToString() {
|
||||||
|
str := ToString([]byte("hello"))
|
||||||
|
println(str) // hello
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleSize() {
|
||||||
|
size := Size([]byte("hello"))
|
||||||
|
println(size) // 5
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleConcatAll() {
|
||||||
|
result := ConcatAll(
|
||||||
|
[]byte("Hello"),
|
||||||
|
[]byte(" "),
|
||||||
|
[]byte("World"),
|
||||||
|
)
|
||||||
|
println(string(result)) // Hello World
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
114
v2/bytes/doc.go
Normal file
114
v2/bytes/doc.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// 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 bytes provides functional programming utilities for working with byte slices.
|
||||||
|
//
|
||||||
|
// This package offers algebraic structures (Monoid, Ord) and utility functions
|
||||||
|
// for byte slice operations in a functional style.
|
||||||
|
//
|
||||||
|
// # Monoid
|
||||||
|
//
|
||||||
|
// The Monoid instance for byte slices combines them through concatenation,
|
||||||
|
// with an empty byte slice as the identity element.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/bytes"
|
||||||
|
//
|
||||||
|
// // Concatenate byte slices
|
||||||
|
// result := bytes.Monoid.Concat([]byte("Hello"), []byte(" World"))
|
||||||
|
// // result: []byte("Hello World")
|
||||||
|
//
|
||||||
|
// // Identity element
|
||||||
|
// empty := bytes.Empty() // []byte{}
|
||||||
|
//
|
||||||
|
// # ConcatAll
|
||||||
|
//
|
||||||
|
// Efficiently concatenates multiple byte slices into a single slice:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/bytes"
|
||||||
|
//
|
||||||
|
// result := bytes.ConcatAll(
|
||||||
|
// []byte("Hello"),
|
||||||
|
// []byte(" "),
|
||||||
|
// []byte("World"),
|
||||||
|
// )
|
||||||
|
// // result: []byte("Hello World")
|
||||||
|
//
|
||||||
|
// # Ordering
|
||||||
|
//
|
||||||
|
// The Ord instance provides lexicographic ordering for byte slices:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/bytes"
|
||||||
|
//
|
||||||
|
// cmp := bytes.Ord.Compare([]byte("abc"), []byte("abd"))
|
||||||
|
// // cmp: -1 (abc < abd)
|
||||||
|
//
|
||||||
|
// equal := bytes.Ord.Equals([]byte("test"), []byte("test"))
|
||||||
|
// // equal: true
|
||||||
|
//
|
||||||
|
// # Utility Functions
|
||||||
|
//
|
||||||
|
// The package provides several utility functions:
|
||||||
|
//
|
||||||
|
// // Get empty byte slice
|
||||||
|
// empty := bytes.Empty() // []byte{}
|
||||||
|
//
|
||||||
|
// // Convert to string
|
||||||
|
// str := bytes.ToString([]byte("hello")) // "hello"
|
||||||
|
//
|
||||||
|
// // Get size
|
||||||
|
// size := bytes.Size([]byte("hello")) // 5
|
||||||
|
//
|
||||||
|
// # Use Cases
|
||||||
|
//
|
||||||
|
// The bytes package is particularly useful for:
|
||||||
|
//
|
||||||
|
// - Building byte buffers functionally
|
||||||
|
// - Combining multiple byte slices efficiently
|
||||||
|
// - Comparing byte slices lexicographically
|
||||||
|
// - Working with binary data in a functional style
|
||||||
|
//
|
||||||
|
// # Example - Building a Protocol Message
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/bytes"
|
||||||
|
// "encoding/binary"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Build a simple protocol message
|
||||||
|
// header := []byte{0x01, 0x02}
|
||||||
|
// length := make([]byte, 4)
|
||||||
|
// binary.BigEndian.PutUint32(length, 100)
|
||||||
|
// payload := []byte("data")
|
||||||
|
//
|
||||||
|
// message := bytes.ConcatAll(header, length, payload)
|
||||||
|
//
|
||||||
|
// # Example - Sorting Byte Slices
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/IBM/fp-go/v2/array"
|
||||||
|
// "github.com/IBM/fp-go/v2/bytes"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// data := [][]byte{
|
||||||
|
// []byte("zebra"),
|
||||||
|
// []byte("apple"),
|
||||||
|
// []byte("mango"),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sorted := array.Sort(bytes.Ord)(data)
|
||||||
|
// // sorted: [[]byte("apple"), []byte("mango"), []byte("zebra")]
|
||||||
|
package bytes
|
||||||
34
v2/bytes/monoid.go
Normal file
34
v2/bytes/monoid.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// 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 bytes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
O "github.com/IBM/fp-go/v2/ord"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// monoid for byte arrays
|
||||||
|
Monoid = A.Monoid[byte]()
|
||||||
|
|
||||||
|
// ConcatAll concatenates all bytes
|
||||||
|
ConcatAll = A.ArrayConcatAll[byte]
|
||||||
|
|
||||||
|
// Ord implements the default ordering on bytes
|
||||||
|
Ord = O.MakeOrd(bytes.Compare, bytes.Equal)
|
||||||
|
)
|
||||||
26
v2/bytes/monoid_test.go
Normal file
26
v2/bytes/monoid_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 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 bytes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMonoid(t *testing.T) {
|
||||||
|
M.AssertLaws(t, Monoid)([][]byte{[]byte(""), []byte("a"), []byte("some value")})
|
||||||
|
}
|
||||||
432
v2/cli/apply.go
Normal file
432
v2/cli/apply.go
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateTraverseTuple(f *os.File, i int) {
|
||||||
|
fmt.Fprintf(f, "\n// TraverseTuple%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i)
|
||||||
|
fmt.Fprintf(f, "// The function takes a [Tuple%d] of base types and %d functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func TraverseTuple%d[\n", i)
|
||||||
|
// map as the starting point
|
||||||
|
fmt.Fprintf(f, " MAP ~func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
fmt.Fprintf(f, "T.")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") func(HKT_T1)")
|
||||||
|
if i > 1 {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := 1; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ",\n")
|
||||||
|
// the applicatives
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " AP%d ~func(", j)
|
||||||
|
fmt.Fprintf(f, "HKT_T%d) func(", j+1)
|
||||||
|
fmt.Fprintf(f, "HKT_F")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")")
|
||||||
|
if j+1 < i {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := j + 1; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ",\n")
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " F%d ~func(A%d) HKT_T%d,\n", j+1, j+1, j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " A%d, T%d,\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", // HKT[")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "func(T%d) ", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T.")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "]\n")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i)
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "]\n")
|
||||||
|
fmt.Fprintf(f, "](\n")
|
||||||
|
|
||||||
|
// the callbacks
|
||||||
|
fmt.Fprintf(f, " fmap MAP,\n")
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " fap%d AP%d,\n", j, j)
|
||||||
|
}
|
||||||
|
// the transformer functions
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " f%d F%d,\n", j, j)
|
||||||
|
}
|
||||||
|
// the parameters
|
||||||
|
fmt.Fprintf(f, " t T.Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "A%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "],\n")
|
||||||
|
fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return F.Pipe%d(\n", i)
|
||||||
|
fmt.Fprintf(f, " f1(t.F1),\n")
|
||||||
|
fmt.Fprintf(f, " fmap(tupleConstructor%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]()),\n")
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " fap%d(f%d(t.F%d)),\n", j, j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSequenceTuple(f *os.File, i int) {
|
||||||
|
fmt.Fprintf(f, "\n// SequenceTuple%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i)
|
||||||
|
fmt.Fprintf(f, "// The function takes a [Tuple%d] of higher higher kinded types and returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func SequenceTuple%d[\n", i)
|
||||||
|
// map as the starting point
|
||||||
|
fmt.Fprintf(f, " MAP ~func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
fmt.Fprintf(f, "T.")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") func(HKT_T1)")
|
||||||
|
if i > 1 {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := 1; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ",\n")
|
||||||
|
// the applicatives
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " AP%d ~func(", j)
|
||||||
|
fmt.Fprintf(f, "HKT_T%d) func(", j+1)
|
||||||
|
fmt.Fprintf(f, "HKT_F")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")")
|
||||||
|
if j+1 < i {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := j + 1; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ",\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", // HKT[")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "func(T%d) ", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T.")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "]\n")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i)
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "]\n")
|
||||||
|
fmt.Fprintf(f, "](\n")
|
||||||
|
|
||||||
|
// the callbacks
|
||||||
|
fmt.Fprintf(f, " fmap MAP,\n")
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " fap%d AP%d,\n", j, j)
|
||||||
|
}
|
||||||
|
// the parameters
|
||||||
|
fmt.Fprintf(f, " t T.Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "HKT_T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "],\n")
|
||||||
|
fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return F.Pipe%d(\n", i)
|
||||||
|
fmt.Fprintf(f, " t.F1,\n")
|
||||||
|
fmt.Fprintf(f, " fmap(tupleConstructor%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]()),\n")
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " fap%d(t.F%d),\n", j, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSequenceT(f *os.File, i int) {
|
||||||
|
fmt.Fprintf(f, "\n// SequenceT%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i)
|
||||||
|
fmt.Fprintf(f, "// The function takes %d higher higher kinded types and returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func SequenceT%d[\n", i)
|
||||||
|
// map as the starting point
|
||||||
|
fmt.Fprintf(f, " MAP ~func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
fmt.Fprintf(f, "T.")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") func(HKT_T1)")
|
||||||
|
if i > 1 {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := 1; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ",\n")
|
||||||
|
// the applicatives
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " AP%d ~func(", j)
|
||||||
|
fmt.Fprintf(f, "HKT_T%d) func(", j+1)
|
||||||
|
fmt.Fprintf(f, "HKT_F")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")")
|
||||||
|
if j+1 < i {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := j + 1; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ",\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " HKT_F")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "_T%d", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", // HKT[")
|
||||||
|
for k := j; k < i; k++ {
|
||||||
|
fmt.Fprintf(f, "func(T%d) ", k+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T.")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "]\n")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i)
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "]\n")
|
||||||
|
fmt.Fprintf(f, "](\n")
|
||||||
|
|
||||||
|
// the callbacks
|
||||||
|
fmt.Fprintf(f, " fmap MAP,\n")
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " fap%d AP%d,\n", j, j)
|
||||||
|
}
|
||||||
|
// the parameters
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d HKT_T%d,\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return F.Pipe%d(\n", i)
|
||||||
|
fmt.Fprintf(f, " t1,\n")
|
||||||
|
fmt.Fprintf(f, " fmap(tupleConstructor%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]()),\n")
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " fap%d(t%d),\n", j, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupleConstructor(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// tupleConstructor%d returns a curried version of [T.MakeTuple%d]\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func tupleConstructor%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any]()")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " T.Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "] {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return F.Curry%d(T.MakeTuple%d[", i, i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "])\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateApplyHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
// print out some helpers
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// tuple constructor
|
||||||
|
generateTupleConstructor(f, i)
|
||||||
|
// sequenceT
|
||||||
|
generateSequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateSequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "apply",
|
||||||
|
Usage: "generate code for the sequence operations of apply",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateApplyHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
294
v2/cli/bind.go
Normal file
294
v2/cli/bind.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createCombinations(n int, all, prev []int) [][]int {
|
||||||
|
l := len(prev)
|
||||||
|
if l == n {
|
||||||
|
return [][]int{prev}
|
||||||
|
}
|
||||||
|
var res [][]int
|
||||||
|
for idx, val := range all {
|
||||||
|
cpy := make([]int, l+1)
|
||||||
|
copy(cpy, prev)
|
||||||
|
cpy[l] = val
|
||||||
|
|
||||||
|
res = append(res, createCombinations(n, all[idx+1:], cpy)...)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func remaining(comb []int, total int) []int {
|
||||||
|
var res []int
|
||||||
|
mp := make(map[int]int)
|
||||||
|
for _, idx := range comb {
|
||||||
|
mp[idx] = idx
|
||||||
|
}
|
||||||
|
for i := 1; i <= total; i++ {
|
||||||
|
_, ok := mp[i]
|
||||||
|
if !ok {
|
||||||
|
res = append(res, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCombSingleBind(f *os.File, comb [][]int, total int) {
|
||||||
|
for _, c := range comb {
|
||||||
|
// remaining indexes
|
||||||
|
rem := remaining(c, total)
|
||||||
|
|
||||||
|
// bind function
|
||||||
|
fmt.Fprintf(f, "\n// Bind")
|
||||||
|
for _, idx := range c {
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "of%d takes a function with %d parameters and returns a new function with %d parameters that will bind these parameters to the positions [", total, total, len(c))
|
||||||
|
for i, idx := range c {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "] of the original function.\n// The return value of is a function with the remaining %d parameters at positions [", len(rem))
|
||||||
|
for i, idx := range rem {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "] of the original function.\n")
|
||||||
|
fmt.Fprintf(f, "func Bind")
|
||||||
|
for _, idx := range c {
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "of%d[F ~func(", total)
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", i+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R")
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", i+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for i, idx := range c {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(")
|
||||||
|
for i, idx := range rem {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
|
||||||
|
for i, idx := range c {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", idx, idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(")
|
||||||
|
for i, idx := range rem {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for i, idx := range rem {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", idx, idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return f(")
|
||||||
|
for i := 1; i <= total; i++ {
|
||||||
|
if i > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
// ignore function
|
||||||
|
fmt.Fprintf(f, "\n// Ignore")
|
||||||
|
for _, idx := range c {
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "of%d takes a function with %d parameters and returns a new function with %d parameters that will ignore the values at positions [", total, len(rem), total)
|
||||||
|
for i, idx := range c {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "] and pass the remaining %d parameters to the original function\n", len(rem))
|
||||||
|
fmt.Fprintf(f, "func Ignore")
|
||||||
|
for _, idx := range c {
|
||||||
|
fmt.Fprintf(f, "%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "of%d[", total)
|
||||||
|
// start with the undefined parameters
|
||||||
|
for i, idx := range c {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", idx)
|
||||||
|
}
|
||||||
|
if len(c) > 0 {
|
||||||
|
fmt.Fprintf(f, " any, ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F ~func(")
|
||||||
|
for i, idx := range rem {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R")
|
||||||
|
for _, idx := range rem {
|
||||||
|
fmt.Fprintf(f, ", T%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", i+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", i+1, i+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return f(")
|
||||||
|
for i, idx := range rem {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", idx)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSingleBind(f *os.File, total int) {
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "// Combinations for a total of %d arguments\n", total)
|
||||||
|
|
||||||
|
// construct the indexes
|
||||||
|
all := make([]int, total)
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
all[i] = i + 1
|
||||||
|
}
|
||||||
|
// for all permutations of a certain length
|
||||||
|
for j := 0; j < total; j++ {
|
||||||
|
// get combinations of that size
|
||||||
|
comb := createCombinations(j+1, all, []int{})
|
||||||
|
generateCombSingleBind(f, comb, total)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateBind(f *os.File, i int) {
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
generateSingleBind(f, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateBindHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n", pkg)
|
||||||
|
|
||||||
|
generateBind(f, count)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BindCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "bind",
|
||||||
|
Usage: "generate code for binder functions etc",
|
||||||
|
Description: "Code generation for bind, etc",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateBindHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
39
v2/cli/commands.go
Normal file
39
v2/cli/commands.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Commands() []*C.Command {
|
||||||
|
return []*C.Command{
|
||||||
|
PipeCommand(),
|
||||||
|
IdentityCommand(),
|
||||||
|
OptionCommand(),
|
||||||
|
EitherCommand(),
|
||||||
|
TupleCommand(),
|
||||||
|
BindCommand(),
|
||||||
|
ApplyCommand(),
|
||||||
|
ContextReaderIOEitherCommand(),
|
||||||
|
ReaderIOEitherCommand(),
|
||||||
|
ReaderCommand(),
|
||||||
|
IOEitherCommand(),
|
||||||
|
IOCommand(),
|
||||||
|
IOOptionCommand(),
|
||||||
|
DICommand(),
|
||||||
|
}
|
||||||
|
}
|
||||||
39
v2/cli/common.go
Normal file
39
v2/cli/common.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyFilename = "filename"
|
||||||
|
keyCount = "count"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flagFilename = &C.StringFlag{
|
||||||
|
Name: keyFilename,
|
||||||
|
Value: "gen.go",
|
||||||
|
Usage: "Name of the generated file",
|
||||||
|
}
|
||||||
|
|
||||||
|
flagCount = &C.IntFlag{
|
||||||
|
Name: keyCount,
|
||||||
|
Value: 20,
|
||||||
|
Usage: "Number of variations to create",
|
||||||
|
}
|
||||||
|
)
|
||||||
271
v2/cli/contextreaderioeither.go
Normal file
271
v2/cli/contextreaderioeither.go
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated:
|
||||||
|
func generateNestedCallbacks(i, total int) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for j := i; j < total; j++ {
|
||||||
|
if j > i {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("func(T%d)", j+1))
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
}
|
||||||
|
buf.WriteString(tupleType("T")(total))
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateNestedCallbacksPlain(i, total int) string {
|
||||||
|
fs := A.MakeBy(total-i, func(j int) string {
|
||||||
|
return fmt.Sprintf("func(T%d)", j+i+1)
|
||||||
|
})
|
||||||
|
ts := A.Of(tupleTypePlain("T")(total))
|
||||||
|
return joinAll(" ")(fs, ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherEitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[R]]\n// The inverse function is [Uneitherize%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Eitherize%d[F ~func(context.Context", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[R] {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Eitherize%d[ReaderIOEither[R]](f)\n", i)
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The inverse function is [Uneitherize%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Eitherize%d[GRA ~func(context.Context) GIOA, F ~func(context.Context", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA {\n")
|
||||||
|
fmt.Fprintf(fg, " return RE.Eitherize%d[GRA](f)\n", i)
|
||||||
|
fmt.Fprintln(fg, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherUneitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a [ReaderIOEither[R]] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the [context.Context].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(context.Context")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Uneitherize%d[ReaderIOEither[R]", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, ", func(context.Context")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")(R, error)](f)\n")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a [GRA] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the [context.Context].\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Uneitherize%d[GRA ~func(context.Context) GIOA, F ~func(context.Context", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", R any](f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA) F {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return func(c context.Context")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return E.UnwrapError(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")(c)())\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonGenericContextReaderIOEither(param string) string {
|
||||||
|
return fmt.Sprintf("ReaderIOEither[%s]", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
var extrasContextReaderIOEither = A.Empty[string]()
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherSequenceT(f *os.File, i int) {
|
||||||
|
generateGenericSequenceT("", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
generateGenericSequenceT("Seq", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
generateGenericSequenceT("Par", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherSequenceTuple(f *os.File, i int) {
|
||||||
|
generateGenericSequenceTuple("", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
generateGenericSequenceTuple("Seq", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
generateGenericSequenceTuple("Par", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherTraverseTuple(f *os.File, i int) {
|
||||||
|
generateGenericTraverseTuple("", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
generateGenericTraverseTuple("Seq", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
generateGenericTraverseTuple("Par", nonGenericContextReaderIOEither, extrasContextReaderIOEither)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContextReaderIOEitherHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// construct subdirectory
|
||||||
|
genFilename := filepath.Join("generic", filename)
|
||||||
|
err = os.MkdirAll("generic", os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fg, err := os.Create(filepath.Clean(genFilename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fg.Close()
|
||||||
|
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
writePackage(f, pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
G "github.com/IBM/fp-go/v2/context/%s/generic"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
`, pkg)
|
||||||
|
|
||||||
|
writePackage(fg, "generic")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, `
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
RE "github.com/IBM/fp-go/v2/readerioeither/generic"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
generateContextReaderIOEitherEitherize(f, fg, 0)
|
||||||
|
generateContextReaderIOEitherUneitherize(f, fg, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// eitherize
|
||||||
|
generateContextReaderIOEitherEitherize(f, fg, i)
|
||||||
|
generateContextReaderIOEitherUneitherize(f, fg, i)
|
||||||
|
// sequenceT
|
||||||
|
generateContextReaderIOEitherSequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateContextReaderIOEitherSequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateContextReaderIOEitherTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextReaderIOEitherCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "contextreaderioeither",
|
||||||
|
Usage: "generate code for ContextReaderIOEither",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateContextReaderIOEitherHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
231
v2/cli/di.go
Normal file
231
v2/cli/di.go
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateMakeProvider(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// MakeProvider%d creates a [DIE.Provider] for an [InjectionToken] from a function with %d dependencies\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeProvider%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
fmt.Fprintf(f, " token InjectionToken[R],\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||||
|
fmt.Fprintf(f, ") DIE.Provider {\n")
|
||||||
|
fmt.Fprint(f, " return DIE.MakeProvider(\n")
|
||||||
|
fmt.Fprint(f, " token,\n")
|
||||||
|
fmt.Fprintf(f, " MakeProviderFactory%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " f,\n")
|
||||||
|
fmt.Fprint(f, " ))\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMakeTokenWithDefault(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// MakeTokenWithDefault%d creates an [InjectionToken] with a default implementation with %d dependencies\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeTokenWithDefault%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
fmt.Fprintf(f, " name string,\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||||
|
fmt.Fprintf(f, ") InjectionToken[R] {\n")
|
||||||
|
fmt.Fprintf(f, " return MakeTokenWithDefault[R](name, MakeProviderFactory%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " f,\n")
|
||||||
|
fmt.Fprint(f, " ))\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMakeProviderFactory(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// MakeProviderFactory%d creates a [DIE.ProviderFactory] from a function with %d arguments and %d dependencies\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeProviderFactory%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R],\n")
|
||||||
|
fmt.Fprintf(f, ") DIE.ProviderFactory {\n")
|
||||||
|
fmt.Fprint(f, " return DIE.MakeProviderFactory(\n")
|
||||||
|
fmt.Fprint(f, " A.From[DIE.Dependency](\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " ),\n")
|
||||||
|
fmt.Fprintf(f, " eraseProviderFactory%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " f,\n")
|
||||||
|
fmt.Fprint(f, " ),\n")
|
||||||
|
fmt.Fprint(f, " )\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEraseProviderFactory(f *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// eraseProviderFactory%d creates a function that takes a variadic number of untyped arguments and from a function of %d strongly typed arguments and %d dependencies\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func eraseProviderFactory%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any, R any](\n")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {\n")
|
||||||
|
fmt.Fprintf(f, " ft := eraseTuple(T.Tupled%d(f))\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d := lookupAt[T%d](%d, d%d)\n", j+1, j+1, j, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " return func(params ...any) IOE.IOEither[error, any] {\n")
|
||||||
|
fmt.Fprintf(f, " return ft(E.SequenceT%d(\n", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d(params),\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprint(f, " ))\n")
|
||||||
|
fmt.Fprint(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateDIHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprint(f, `
|
||||||
|
import (
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
generateEraseProviderFactory(f, i)
|
||||||
|
generateMakeProviderFactory(f, i)
|
||||||
|
generateMakeTokenWithDefault(f, i)
|
||||||
|
generateMakeProvider(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DICommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "di",
|
||||||
|
Usage: "generate code for the dependency injection package",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateDIHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
200
v2/cli/either.go
Normal file
200
v2/cli/either.go
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func eitherHKT(typeE string) func(typeA string) string {
|
||||||
|
return func(typeA string) string {
|
||||||
|
return fmt.Sprintf("Either[%s, %s]", typeE, typeA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEitherTraverseTuple(f *os.File, i int) {
|
||||||
|
generateTraverseTuple1(eitherHKT("E"), "E")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEitherSequenceTuple(f *os.File, i int) {
|
||||||
|
generateSequenceTuple1(eitherHKT("E"), "E")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEitherSequenceT(f *os.File, i int) {
|
||||||
|
generateSequenceT1(eitherHKT("E"), "E")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUneitherize(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning an Either into a function with %d parameters returning a tuple\n// The inverse function is [Eitherize%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return UnwrapError(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, "))")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEitherize(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning an Either\n// The inverse function is [Uneitherize%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Eitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Either[error, R] {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Either[error, R] {\n")
|
||||||
|
fmt.Fprintf(f, " return TryCatchError(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, "))")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEitherHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// zero level functions
|
||||||
|
|
||||||
|
// optionize
|
||||||
|
generateEitherize(f, 0)
|
||||||
|
// unoptionize
|
||||||
|
generateUneitherize(f, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// optionize
|
||||||
|
generateEitherize(f, i)
|
||||||
|
// unoptionize
|
||||||
|
generateUneitherize(f, i)
|
||||||
|
// sequenceT
|
||||||
|
generateEitherSequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateEitherSequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateEitherTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EitherCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "either",
|
||||||
|
Usage: "generate code for Either",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateEitherHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
31
v2/cli/header.go
Normal file
31
v2/cli/header.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writePackage(f *os.File, pkg string) {
|
||||||
|
// print package
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
}
|
||||||
103
v2/cli/identity.go
Normal file
103
v2/cli/identity.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func identityHKT(typeA string) string {
|
||||||
|
return typeA
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIdentityTraverseTuple(f *os.File, i int) {
|
||||||
|
generateTraverseTuple1(identityHKT, "")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIdentitySequenceTuple(f *os.File, i int) {
|
||||||
|
generateSequenceTuple1(identityHKT, "")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIdentitySequenceT(f *os.File, i int) {
|
||||||
|
generateSequenceT1(identityHKT, "")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIdentityHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// sequenceT
|
||||||
|
generateIdentitySequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateIdentitySequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateIdentityTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IdentityCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "identity",
|
||||||
|
Usage: "generate code for Identity",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateIdentityHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
112
v2/cli/io.go
Normal file
112
v2/cli/io.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func nonGenericIO(param string) string {
|
||||||
|
return fmt.Sprintf("IO[%s]", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
var extrasIO = A.Empty[string]()
|
||||||
|
|
||||||
|
func generateIOSequenceT(f *os.File, i int) {
|
||||||
|
generateGenericSequenceT("", nonGenericIO, extrasIO)(f, i)
|
||||||
|
generateGenericSequenceT("Seq", nonGenericIO, extrasIO)(f, i)
|
||||||
|
generateGenericSequenceT("Par", nonGenericIO, extrasIO)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOSequenceTuple(f *os.File, i int) {
|
||||||
|
generateGenericSequenceTuple("", nonGenericIO, extrasIO)(f, i)
|
||||||
|
generateGenericSequenceTuple("Seq", nonGenericIO, extrasIO)(f, i)
|
||||||
|
generateGenericSequenceTuple("Par", nonGenericIO, extrasIO)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOTraverseTuple(f *os.File, i int) {
|
||||||
|
generateGenericTraverseTuple("", nonGenericIO, extrasIO)(f, i)
|
||||||
|
generateGenericTraverseTuple("Seq", nonGenericIO, extrasIO)(f, i)
|
||||||
|
generateGenericTraverseTuple("Par", nonGenericIO, extrasIO)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// sequenceT
|
||||||
|
generateIOSequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateIOSequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateIOTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IOCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "io",
|
||||||
|
Usage: "generate code for IO",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateIOHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
283
v2/cli/ioeither.go
Normal file
283
v2/cli/ioeither.go
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// [GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GTAB ~func() ET.Either[E, T.Tuple2[A, B]], E, A, B any](a GA, b GB) GTAB {
|
||||||
|
|
||||||
|
func nonGenericIOEither(param string) string {
|
||||||
|
return fmt.Sprintf("IOEither[E, %s]", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
var extrasIOEither = A.From("E")
|
||||||
|
|
||||||
|
func generateIOEitherSequenceT(f, fg *os.File, i int) {
|
||||||
|
generateGenericSequenceT("", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
generateGenericSequenceT("Seq", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
generateGenericSequenceT("Par", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOEitherSequenceTuple(f, fg *os.File, i int) {
|
||||||
|
generateGenericSequenceTuple("", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
generateGenericSequenceTuple("Seq", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
generateGenericSequenceTuple("Par", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOEitherTraverseTuple(f, fg *os.File, i int) {
|
||||||
|
generateGenericTraverseTuple("", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
generateGenericTraverseTuple("Seq", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
generateGenericTraverseTuple("Par", nonGenericIOEither, extrasIOEither)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOEitherUneitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [IOEither[error, R]]\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOEither[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Uneitherize%d[IOEither[error, R]](f)\n", i)
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GIOA]\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Uneitherize%d[GIOA ~func() ET.Either[error, R], GTA ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GIOA")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", R any](f GTA) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d T%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return ET.Unwrap(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")())\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOEitherEitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [IOEither[error, R]]\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Eitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") IOEither[error, R] {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Eitherize%d[IOEither[error, R]](f)\n", i)
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GIOA]\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Eitherize%d[GIOA ~func() ET.Either[error, R], F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GIOA {\n")
|
||||||
|
fmt.Fprintf(fg, " e := ET.Eitherize%d(f)\n", i)
|
||||||
|
fmt.Fprintf(fg, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d T%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GIOA {\n")
|
||||||
|
fmt.Fprintf(fg, " return func() ET.Either[error, R] {\n")
|
||||||
|
fmt.Fprintf(fg, " return e(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")\n")
|
||||||
|
fmt.Fprintf(fg, " }}\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOEitherHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// construct subdirectory
|
||||||
|
genFilename := filepath.Join("generic", filename)
|
||||||
|
err = os.MkdirAll("generic", os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fg, err := os.Create(filepath.Clean(genFilename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fg.Close()
|
||||||
|
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/%s/generic"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
`, pkg)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(fg, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(fg, "// %s\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, "package generic\n\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, `
|
||||||
|
import (
|
||||||
|
ET "github.com/IBM/fp-go/v2/either"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// eitherize
|
||||||
|
generateIOEitherEitherize(f, fg, 0)
|
||||||
|
// uneitherize
|
||||||
|
generateIOEitherUneitherize(f, fg, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// eitherize
|
||||||
|
generateIOEitherEitherize(f, fg, i)
|
||||||
|
// uneitherize
|
||||||
|
generateIOEitherUneitherize(f, fg, i)
|
||||||
|
// sequenceT
|
||||||
|
generateIOEitherSequenceT(f, fg, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateIOEitherSequenceTuple(f, fg, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateIOEitherTraverseTuple(f, fg, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IOEitherCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "ioeither",
|
||||||
|
Usage: "generate code for IOEither",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateIOEitherHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
117
v2/cli/iooption.go
Normal file
117
v2/cli/iooption.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func nonGenericIOOption(param string) string {
|
||||||
|
return fmt.Sprintf("IOOption[%s]", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genericIOOption(param string) string {
|
||||||
|
return fmt.Sprintf("func() O.Option[%s]", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
var extrasIOOption = A.Empty[string]()
|
||||||
|
|
||||||
|
func generateIOOptionSequenceT(f *os.File, i int) {
|
||||||
|
generateGenericSequenceT("", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
generateGenericSequenceT("Seq", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
generateGenericSequenceT("Par", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOOptionSequenceTuple(f *os.File, i int) {
|
||||||
|
generateGenericSequenceTuple("", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
generateGenericSequenceTuple("Seq", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
generateGenericSequenceTuple("Par", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOOptionTraverseTuple(f *os.File, i int) {
|
||||||
|
generateGenericTraverseTuple("", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
generateGenericTraverseTuple("Seq", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
generateGenericTraverseTuple("Par", nonGenericIOOption, extrasIOOption)(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIOOptionHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// sequenceT
|
||||||
|
generateIOOptionSequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateIOOptionSequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateIOOptionTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IOOptionCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "iooption",
|
||||||
|
Usage: "generate code for IOOption",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateIOOptionHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
376
v2/cli/monad.go
Normal file
376
v2/cli/monad.go
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated:
|
||||||
|
func tupleType(name string) func(i int) string {
|
||||||
|
return func(i int) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.WriteString(fmt.Sprintf("T.Tuple%d[", i))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s%d", name, j+1))
|
||||||
|
}
|
||||||
|
buf.WriteString("]")
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tupleTypePlain(name string) func(i int) string {
|
||||||
|
return func(i int) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.WriteString(fmt.Sprintf("tuple.Tuple%d[", i))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s%d", name, j+1))
|
||||||
|
}
|
||||||
|
buf.WriteString("]")
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func monadGenerateSequenceTNonGeneric(
|
||||||
|
hkt func(string) string,
|
||||||
|
fmap func(string, string) string,
|
||||||
|
fap func(string, string) string,
|
||||||
|
) func(f *os.File, i int) {
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "SequenceT%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "](")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") %s {", hkt(tuple))
|
||||||
|
// the actual apply callback
|
||||||
|
fmt.Fprintf(f, " return apply.SequenceT%d(\n", i)
|
||||||
|
// map callback
|
||||||
|
|
||||||
|
curried := func(count int) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for j := count; j < i; j++ {
|
||||||
|
buf.WriteString(fmt.Sprintf("func(T%d)", j+1))
|
||||||
|
}
|
||||||
|
buf.WriteString(tuple)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " %s,\n", fmap("T1", curried(1)))
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " %s,\n", fap(curried(j+1), fmt.Sprintf("T%d", j)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func monadGenerateSequenceTGeneric(
|
||||||
|
hkt func(string) string,
|
||||||
|
fmap func(string, string) string,
|
||||||
|
fap func(string, string) string,
|
||||||
|
) func(f *os.File, i int) {
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "SequenceT%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "](")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") %s {", hkt(tuple))
|
||||||
|
// the actual apply callback
|
||||||
|
fmt.Fprintf(f, " return apply.SequenceT%d(\n", i)
|
||||||
|
// map callback
|
||||||
|
|
||||||
|
curried := func(count int) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for j := count; j < i; j++ {
|
||||||
|
buf.WriteString(fmt.Sprintf("func(T%d)", j+1))
|
||||||
|
}
|
||||||
|
buf.WriteString(tuple)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " %s,\n", fmap("T1", curried(1)))
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " %s,\n", fap(curried(j+1), fmt.Sprintf("T%d", j)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " T%d,\n", j+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTraverseTuple1(
|
||||||
|
hkt func(string) string,
|
||||||
|
infix string) func(f *os.File, i int) {
|
||||||
|
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "\n// TraverseTuple%d converts a [Tuple%d] of [A] via transformation functions transforming [A] to [%s] into a [%s].\n", i, i, hkt("A"), hkt(fmt.Sprintf("Tuple%d", i)))
|
||||||
|
fmt.Fprintf(f, "func TraverseTuple%d[", i)
|
||||||
|
// functions
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F%d ~func(A%d) %s", j+1, j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, ", %s", infix)
|
||||||
|
}
|
||||||
|
// types
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", A%d, T%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d F%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func (T.Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "A%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) %s {\n", hkt(tuple))
|
||||||
|
fmt.Fprintf(f, " return func(t T.Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "A%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) %s {\n", hkt(tuple))
|
||||||
|
fmt.Fprintf(f, " return A.TraverseTuple%d(\n", i)
|
||||||
|
// map
|
||||||
|
fmt.Fprintf(f, " Map[")
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, "%s, T1,", infix)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, "T1,")
|
||||||
|
}
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " %s],\n", tuple)
|
||||||
|
// applicatives
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " Ap[")
|
||||||
|
for k := j + 1; k < i; k++ {
|
||||||
|
if k > j+1 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "func(T%d)", k+1)
|
||||||
|
}
|
||||||
|
if j < i-1 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s", tuple)
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, ", %s", infix)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", T%d],\n", j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " f%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " t,\n")
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSequenceTuple1(
|
||||||
|
hkt func(string) string,
|
||||||
|
infix string) func(f *os.File, i int) {
|
||||||
|
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "\n// SequenceTuple%d converts a [Tuple%d] of [%s] into an [%s].\n", i, i, hkt("T"), hkt(fmt.Sprintf("Tuple%d", i)))
|
||||||
|
fmt.Fprintf(f, "func SequenceTuple%d[", i)
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, "%s", infix)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if infix != "" || j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](t T.Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s", hkt(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) %s {\n", hkt(tuple))
|
||||||
|
fmt.Fprintf(f, " return A.SequenceTuple%d(\n", i)
|
||||||
|
// map
|
||||||
|
fmt.Fprintf(f, " Map[")
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, "%s, T1,", infix)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, "T1,")
|
||||||
|
}
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " %s],\n", tuple)
|
||||||
|
// applicatives
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " Ap[")
|
||||||
|
for k := j + 1; k < i; k++ {
|
||||||
|
if k > j+1 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "func(T%d)", k+1)
|
||||||
|
}
|
||||||
|
if j < i-1 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s", tuple)
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, ", %s", infix)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", T%d],\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " t,\n")
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSequenceT1(
|
||||||
|
hkt func(string) string,
|
||||||
|
infix string) func(f *os.File, i int) {
|
||||||
|
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "\n// SequenceT%d converts %d parameters of [%s] into a [%s].\n", i, i, hkt("T"), hkt(fmt.Sprintf("Tuple%d", i)))
|
||||||
|
fmt.Fprintf(f, "func SequenceT%d[", i)
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, "%s", infix)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if infix != "" || j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") %s {\n", hkt(tuple))
|
||||||
|
fmt.Fprintf(f, " return A.SequenceT%d(\n", i)
|
||||||
|
// map
|
||||||
|
fmt.Fprintf(f, " Map[")
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, "%s, T1,", infix)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, "T1,")
|
||||||
|
}
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " %s],\n", tuple)
|
||||||
|
// applicatives
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " Ap[")
|
||||||
|
for k := j + 1; k < i; k++ {
|
||||||
|
if k > j+1 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "func(T%d)", k+1)
|
||||||
|
}
|
||||||
|
if j < i-1 {
|
||||||
|
fmt.Fprintf(f, " ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s", tuple)
|
||||||
|
if infix != "" {
|
||||||
|
fmt.Fprintf(f, ", %s", infix)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", T%d],\n", j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d,\n", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
413
v2/cli/monad2.go
Normal file
413
v2/cli/monad2.go
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
N "github.com/IBM/fp-go/v2/number"
|
||||||
|
S "github.com/IBM/fp-go/v2/string"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
concStrgs = A.Monoid[string]().Concat
|
||||||
|
intercalStrgs = A.Intercalate(S.Monoid)
|
||||||
|
concAllStrgs = A.ConcatAll(A.Monoid[string]())
|
||||||
|
)
|
||||||
|
|
||||||
|
func joinAll(middle string) func(all ...[]string) string {
|
||||||
|
ic := intercalStrgs(middle)
|
||||||
|
return func(all ...[]string) string {
|
||||||
|
return ic(concAllStrgs(all))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated:
|
||||||
|
func deprecatedGenerateGenericSequenceT(
|
||||||
|
nonGenericType func(string) string,
|
||||||
|
genericType func(string) string,
|
||||||
|
extra []string,
|
||||||
|
) func(f, fg *os.File, i int) {
|
||||||
|
return func(f, fg *os.File, i int) {
|
||||||
|
// tuple
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
// all types T
|
||||||
|
typesT := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("T%d"),
|
||||||
|
))
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// SequenceT%d converts %d [%s] into a [%s]\n", i, i, nonGenericType("T"), nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, "func SequenceT%d[%s any](\n", i, joinAll(", ")(extra, typesT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d %s,\n", j+1, nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") %s {\n", nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, " return G.SequenceT%d[\n", i)
|
||||||
|
fmt.Fprintf(f, " %s,\n", nonGenericType(tuple))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " %s,\n", nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " ](")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// SequenceT%d converts %d [%s] into a [%s]\n", i, i, genericType("T"), genericType(tuple))
|
||||||
|
fmt.Fprintf(fg, "func SequenceT%d[\n", i)
|
||||||
|
fmt.Fprintf(fg, " G_TUPLE%d ~%s,\n", i, genericType(tuple))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " G_T%d ~%s, \n", j+1, genericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " %s any](\n", joinAll(", ")(extra, typesT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " t%d G_T%d,\n", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") G_TUPLE%d {\n", i)
|
||||||
|
fmt.Fprintf(fg, " return A.SequenceT%d(\n", i)
|
||||||
|
// map call
|
||||||
|
var cio string
|
||||||
|
cb := generateNestedCallbacks(1, i)
|
||||||
|
if i > 1 {
|
||||||
|
cio = genericType(cb)
|
||||||
|
} else {
|
||||||
|
cio = fmt.Sprintf("G_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " Map[%s],\n", joinAll(", ")(A.From("G_T1", cio), extra, A.From("T1", cb)))
|
||||||
|
// the apply calls
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
if j < i-1 {
|
||||||
|
cb := generateNestedCallbacks(j+1, i)
|
||||||
|
cio = genericType(cb)
|
||||||
|
} else {
|
||||||
|
cio = fmt.Sprintf("G_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " Ap[%s, %s, G_T%d],\n", cio, genericType(generateNestedCallbacks(j, i)), j+1)
|
||||||
|
}
|
||||||
|
// function parameters
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " t%d,\n", j+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " )\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated:
|
||||||
|
func deprecatedGenerateGenericSequenceTuple(
|
||||||
|
nonGenericType func(string) string,
|
||||||
|
genericType func(string) string,
|
||||||
|
extra []string,
|
||||||
|
) func(f, fg *os.File, i int) {
|
||||||
|
return func(f, fg *os.File, i int) {
|
||||||
|
// tuple
|
||||||
|
tuple := tupleType("T")(i)
|
||||||
|
// all types T
|
||||||
|
typesT := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("T%d"),
|
||||||
|
))
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// SequenceTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, nonGenericType("T"), nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, "func SequenceTuple%d[%s any](t T.Tuple%d[", i, joinAll(", ")(extra, typesT), i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s", nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) %s {\n", nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, " return G.SequenceTuple%d[\n", i)
|
||||||
|
fmt.Fprintf(f, " %s,\n", nonGenericType(tuple))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " %s,\n", nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " ](t)\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// SequenceTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, genericType("T"), genericType(tuple))
|
||||||
|
fmt.Fprintf(fg, "func SequenceTuple%d[\n", i)
|
||||||
|
fmt.Fprintf(fg, " G_TUPLE%d ~%s,\n", i, genericType(tuple))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " G_T%d ~%s, \n", j+1, genericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " %s any](t T.Tuple%d[", joinAll(", ")(extra, typesT), i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "G_T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "]) G_TUPLE%d {\n", i)
|
||||||
|
fmt.Fprintf(fg, " return A.SequenceTuple%d(\n", i)
|
||||||
|
// map call
|
||||||
|
var cio string
|
||||||
|
cb := generateNestedCallbacks(1, i)
|
||||||
|
if i > 1 {
|
||||||
|
cio = genericType(cb)
|
||||||
|
} else {
|
||||||
|
cio = fmt.Sprintf("G_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " Map[%s],\n", joinAll(", ")(A.From("G_T1", cio), extra, A.From("T1", cb)))
|
||||||
|
// the apply calls
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
if j < i-1 {
|
||||||
|
cb := generateNestedCallbacks(j+1, i)
|
||||||
|
cio = genericType(cb)
|
||||||
|
} else {
|
||||||
|
cio = fmt.Sprintf("G_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " Ap[%s, %s, G_T%d],\n", cio, genericType(generateNestedCallbacks(j, i)), j+1)
|
||||||
|
}
|
||||||
|
// function parameters
|
||||||
|
fmt.Fprintf(fg, " t)\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated:
|
||||||
|
func deprecatedGenerateGenericTraverseTuple(
|
||||||
|
nonGenericType func(string) string,
|
||||||
|
genericType func(string) string,
|
||||||
|
extra []string,
|
||||||
|
) func(f, fg *os.File, i int) {
|
||||||
|
return func(f, fg *os.File, i int) {
|
||||||
|
// tuple
|
||||||
|
tupleT := tupleType("T")(i)
|
||||||
|
tupleA := tupleType("A")(i)
|
||||||
|
// all types T
|
||||||
|
typesT := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("T%d"),
|
||||||
|
))
|
||||||
|
// all types A
|
||||||
|
typesA := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("A%d"),
|
||||||
|
))
|
||||||
|
// all function types
|
||||||
|
typesF := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
func(j int) string {
|
||||||
|
return fmt.Sprintf("F%d ~func(A%d) %s", j, j, nonGenericType(fmt.Sprintf("T%d", j)))
|
||||||
|
},
|
||||||
|
))
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// TraverseTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, nonGenericType("T"), nonGenericType(tupleT))
|
||||||
|
fmt.Fprintf(f, "func TraverseTuple%d[%s any](", i, joinAll(", ")(typesF, extra, typesA, typesT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d F%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(%s) %s {\n", tupleA, nonGenericType(tupleT))
|
||||||
|
fmt.Fprintf(f, " return G.TraverseTuple%d[%s](", i, nonGenericType(tupleT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// TraverseTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, genericType("T"), genericType(tupleT))
|
||||||
|
fmt.Fprintf(fg, "func TraverseTuple%d[\n", i)
|
||||||
|
fmt.Fprintf(fg, " G_TUPLE%d ~%s,\n", i, genericType(tupleT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " F%d ~func(A%d) G_T%d,\n", j+1, j+1, j+1)
|
||||||
|
}
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " G_T%d ~%s, \n", j+1, genericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " %s any](", joinAll(", ")(extra, typesA, typesT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "f%d F%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") func(%s) G_TUPLE%d {\n", tupleA, i)
|
||||||
|
fmt.Fprintf(fg, " return func(t %s) G_TUPLE%d {\n", tupleA, i)
|
||||||
|
fmt.Fprintf(fg, " return A.TraverseTuple%d(\n", i)
|
||||||
|
// map call
|
||||||
|
var cio string
|
||||||
|
cb := generateNestedCallbacks(1, i)
|
||||||
|
if i > 1 {
|
||||||
|
cio = genericType(cb)
|
||||||
|
} else {
|
||||||
|
cio = fmt.Sprintf("G_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " Map[%s],\n", joinAll(", ")(A.From("G_T1", cio), extra, A.From("T1", cb)))
|
||||||
|
// the apply calls
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
if j < i-1 {
|
||||||
|
cb := generateNestedCallbacks(j+1, i)
|
||||||
|
cio = genericType(cb)
|
||||||
|
} else {
|
||||||
|
cio = fmt.Sprintf("G_TUPLE%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, " Ap[%s, %s, G_T%d],\n", cio, genericType(generateNestedCallbacks(j, i)), j+1)
|
||||||
|
}
|
||||||
|
// function parameters
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, " f%d,\n", j+1)
|
||||||
|
}
|
||||||
|
// tuple parameter
|
||||||
|
fmt.Fprintf(fg, " t)\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateGenericSequenceT(
|
||||||
|
suffix string,
|
||||||
|
nonGenericType func(string) string,
|
||||||
|
extra []string,
|
||||||
|
) func(f *os.File, i int) {
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
// tuple
|
||||||
|
tuple := tupleTypePlain("T")(i)
|
||||||
|
// all types T
|
||||||
|
typesT := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("T%d"),
|
||||||
|
))
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Sequence%sT%d converts %d [%s] into a [%s]\n", suffix, i, i, nonGenericType("T"), nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, "func Sequence%sT%d[%s any](\n", suffix, i, joinAll(", ")(extra, typesT))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d %s,\n", j+1, nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") %s {\n", nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, " return apply.SequenceT%d(\n", i)
|
||||||
|
fmt.Fprintf(f, " Map[%s],\n", joinAll(", ")(extra, A.From("T1", generateNestedCallbacksPlain(1, i))))
|
||||||
|
|
||||||
|
// the apply calls
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " Ap%s[%s],\n", suffix, joinAll(", ")(A.Of(generateNestedCallbacksPlain(j, i)), extra, A.Of(fmt.Sprintf("T%d", j))))
|
||||||
|
}
|
||||||
|
// function parameters
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " t%d,\n", j)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateGenericSequenceTuple(
|
||||||
|
suffix string,
|
||||||
|
nonGenericType func(string) string,
|
||||||
|
extra []string,
|
||||||
|
) func(f *os.File, i int) {
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
// tuple
|
||||||
|
tuple := tupleTypePlain("T")(i)
|
||||||
|
// all types T
|
||||||
|
typesT := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("T%d"),
|
||||||
|
))
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Sequence%sTuple%d converts a [tuple.Tuple%d[%s]] into a [%s]\n", suffix, i, i, nonGenericType("T"), nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, "func Sequence%sTuple%d[%s any](t tuple.Tuple%d[", suffix, i, joinAll(", ")(extra, typesT), i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s", nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) %s {\n", nonGenericType(tuple))
|
||||||
|
fmt.Fprintf(f, " return apply.SequenceTuple%d(\n", i)
|
||||||
|
fmt.Fprintf(f, " Map[%s],\n", joinAll(", ")(extra, A.From("T1", generateNestedCallbacksPlain(1, i))))
|
||||||
|
|
||||||
|
// the apply calls
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " Ap%s[%s],\n", suffix, joinAll(", ")(A.Of(generateNestedCallbacksPlain(j, i)), extra, A.Of(fmt.Sprintf("T%d", j))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// function parameters
|
||||||
|
fmt.Fprintf(f, " t,\n")
|
||||||
|
// function parameters
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateGenericTraverseTuple(
|
||||||
|
suffix string,
|
||||||
|
nonGenericType func(string) string,
|
||||||
|
extra []string,
|
||||||
|
) func(f *os.File, i int) {
|
||||||
|
return func(f *os.File, i int) {
|
||||||
|
// all types T
|
||||||
|
typesT := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("T%d"),
|
||||||
|
))
|
||||||
|
// all types A
|
||||||
|
typesA := A.MakeBy(i, F.Flow2(
|
||||||
|
N.Inc[int],
|
||||||
|
S.Format[int]("A%d"),
|
||||||
|
))
|
||||||
|
// function types
|
||||||
|
typesF := A.MakeBy(i, func(j int) string {
|
||||||
|
return fmt.Sprintf("F%d ~func(A%d) %s", j+1, j+1, nonGenericType(fmt.Sprintf("T%d", j+1)))
|
||||||
|
})
|
||||||
|
paramF := A.MakeBy(i, func(j int) string {
|
||||||
|
return fmt.Sprintf("f%d F%d", j+1, j+1)
|
||||||
|
})
|
||||||
|
// return type
|
||||||
|
paramType := fmt.Sprintf("tuple.Tuple%d[%s]", i, joinAll(", ")(typesA))
|
||||||
|
returnType := nonGenericType(fmt.Sprintf("tuple.Tuple%d[%s]", i, joinAll(", ")(typesT)))
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Traverse%sTuple%d converts a [%s] into a [%s]\n", suffix, i, paramType, returnType)
|
||||||
|
fmt.Fprintf(f, "func Traverse%sTuple%d[%s any](%s) func(%s) %s {\n", suffix, i, joinAll(", ")(extra, typesF, typesT, typesA), joinAll(", ")(paramF), paramType, returnType)
|
||||||
|
fmt.Fprintf(f, " return func(t %s) %s {\n", paramType, returnType)
|
||||||
|
fmt.Fprintf(f, " return apply.TraverseTuple%d(\n", i)
|
||||||
|
fmt.Fprintf(f, " Map[%s],\n", joinAll(", ")(extra, A.From("T1", generateNestedCallbacksPlain(1, i))))
|
||||||
|
|
||||||
|
// the apply calls
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " Ap%s[%s],\n", suffix, joinAll(", ")(A.Of(generateNestedCallbacksPlain(j, i)), extra, A.Of(fmt.Sprintf("T%d", j))))
|
||||||
|
}
|
||||||
|
// the function parameters
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " f%d,\n", j)
|
||||||
|
}
|
||||||
|
// function parameters
|
||||||
|
fmt.Fprintf(f, " t,\n")
|
||||||
|
// function parameters
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
210
v2/cli/option.go
Normal file
210
v2/cli/option.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func optionHKT(typeA string) string {
|
||||||
|
return fmt.Sprintf("Option[%s]", typeA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOptionTraverseTuple(f *os.File, i int) {
|
||||||
|
generateTraverseTuple1(optionHKT, "")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOptionSequenceTuple(f *os.File, i int) {
|
||||||
|
generateSequenceTuple1(optionHKT, "")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOptionSequenceT(f *os.File, i int) {
|
||||||
|
generateSequenceT1(optionHKT, "")(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOptionize(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Optionize%d converts a function with %d parameters returning a tuple of a return value R and a boolean into a function with %d parameters returning an Option[R]\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Optionize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, bool)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Option[R] {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Option[R] {\n")
|
||||||
|
fmt.Fprintf(f, " return optionize(func() (R, bool) {\n")
|
||||||
|
fmt.Fprintf(f, " return f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, ")")
|
||||||
|
fmt.Fprintln(f, " })")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUnoptionize(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Unoptionize%d converts a function with %d parameters returning a tuple of a return value R and a boolean into a function with %d parameters returning an Option[R]\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Unoptionize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Option[R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, bool) {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, bool) {\n")
|
||||||
|
fmt.Fprintf(f, " return Unwrap(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, "))")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOptionHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
A "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// print out some helpers
|
||||||
|
fmt.Fprintf(f, `// optionize converts a nullary function to an option
|
||||||
|
func optionize[R any](f func() (R, bool)) Option[R] {
|
||||||
|
if r, ok := f(); ok {
|
||||||
|
return Some(r)
|
||||||
|
}
|
||||||
|
return None[R]()
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
// zero level functions
|
||||||
|
|
||||||
|
// optionize
|
||||||
|
generateOptionize(f, 0)
|
||||||
|
// unoptionize
|
||||||
|
generateUnoptionize(f, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// optionize
|
||||||
|
generateOptionize(f, i)
|
||||||
|
// unoptionize
|
||||||
|
generateUnoptionize(f, i)
|
||||||
|
// sequenceT
|
||||||
|
generateOptionSequenceT(f, i)
|
||||||
|
// sequenceTuple
|
||||||
|
generateOptionSequenceTuple(f, i)
|
||||||
|
// traverseTuple
|
||||||
|
generateOptionTraverseTuple(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func OptionCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "option",
|
||||||
|
Usage: "generate code for Option",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateOptionHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
433
v2/cli/pipe.go
Normal file
433
v2/cli/pipe.go
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateUnsliced(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Unsliced%d converts a function taking a slice parameter into a function with %d parameters\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func Unsliced%d[F ~func([]T) R, T, R any](f F) func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j+1)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, " T")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return f([]T{")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, "})")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateVariadic(f *os.File, i int) {
|
||||||
|
// Create the nullary version
|
||||||
|
fmt.Fprintf(f, "\n// Variadic%d converts a function taking %d parameters and a final slice into a function with %d parameters but a final variadic argument\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Variadic%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "V, R any](f func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "[]V) R) func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "...V) R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "v ...V) R {\n")
|
||||||
|
fmt.Fprintf(f, " return f(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "v)\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUnvariadic(f *os.File, i int) {
|
||||||
|
// Create the nullary version
|
||||||
|
fmt.Fprintf(f, "\n// Unvariadic%d converts a function taking %d parameters and a final variadic argument into a function with %d parameters but a final slice argument\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Unvariadic%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "V, R any](f func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "...V) R) func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "[]V) R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "v []V) R {\n")
|
||||||
|
fmt.Fprintf(f, " return f(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "v...)\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateNullary(f *os.File, i int) {
|
||||||
|
// Create the nullary version
|
||||||
|
fmt.Fprintf(f, "\n// Nullary%d creates a parameter less function from a parameter less function and %d functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions\n", i, i-1)
|
||||||
|
fmt.Fprintf(f, "func Nullary%d[F1 ~func() T1", i)
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", F%d ~func(T%d) T%d", j, j-1, j)
|
||||||
|
}
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](f1 F1")
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", f%d F%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func() T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return func() T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return Pipe%d(f1()", i-1)
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", f%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFlow(f *os.File, i int) {
|
||||||
|
// Create the flow version
|
||||||
|
fmt.Fprintf(f, "\n// Flow%d creates a function that takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func Flow%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j)
|
||||||
|
}
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d F%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(T0) T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return func(t0 T0) T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return Pipe%d(t0", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", f%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePipe(f *os.File, i int) {
|
||||||
|
// Create the pipe version
|
||||||
|
fmt.Fprintf(f, "\n// Pipe%d takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func Pipe%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j)
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " any](t0 T0")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", f%d F%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return ")
|
||||||
|
for j := i; j >= 1; j-- {
|
||||||
|
fmt.Fprintf(f, "f%d(", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t0")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ")")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "\n")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func recurseCurry(f *os.File, indent string, total, count int) {
|
||||||
|
if count == 1 {
|
||||||
|
fmt.Fprintf(f, "%sreturn func(t%d T%d) T%d {\n", indent, total-1, total-1, total)
|
||||||
|
fmt.Fprintf(f, "%s return f(t0", indent)
|
||||||
|
for i := 1; i < total; i++ {
|
||||||
|
fmt.Fprintf(f, ", t%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "%s}\n", indent)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, "%sreturn", indent)
|
||||||
|
for i := total - count + 1; i <= total; i++ {
|
||||||
|
fmt.Fprintf(f, " func(t%d T%d)", i-1, i-1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " T%d {\n", total)
|
||||||
|
recurseCurry(f, fmt.Sprintf(" %s", indent), total, count-1)
|
||||||
|
fmt.Fprintf(f, "%s}\n", indent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCurry(f *os.File, i int) {
|
||||||
|
// Create the curry version
|
||||||
|
fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Curry%d[FCT ~func(T0", i)
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") T%d", i)
|
||||||
|
// type arguments
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](f FCT) func(T0)")
|
||||||
|
for j := 2; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j-1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " T%d {\n", i)
|
||||||
|
recurseCurry(f, " ", i, i)
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUncurry(f *os.File, i int) {
|
||||||
|
// Create the uncurry version
|
||||||
|
fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Uncurry%d[FCT ~func(T0)", i)
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, " func(T%d)", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " T%d", i)
|
||||||
|
// the type parameters
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](f FCT) func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j-1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j-1, j-1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") T%d {\n", i)
|
||||||
|
fmt.Fprintf(f, " return f")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, "(t%d)", j-1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePipeHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n", pkg)
|
||||||
|
|
||||||
|
// pipe
|
||||||
|
generatePipe(f, 0)
|
||||||
|
// variadic
|
||||||
|
generateVariadic(f, 0)
|
||||||
|
// unvariadic
|
||||||
|
generateUnvariadic(f, 0)
|
||||||
|
// unsliced
|
||||||
|
generateUnsliced(f, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
|
||||||
|
// pipe
|
||||||
|
generatePipe(f, i)
|
||||||
|
// flow
|
||||||
|
generateFlow(f, i)
|
||||||
|
// nullary
|
||||||
|
generateNullary(f, i)
|
||||||
|
// curry
|
||||||
|
generateCurry(f, i)
|
||||||
|
// uncurry
|
||||||
|
generateUncurry(f, i)
|
||||||
|
// variadic
|
||||||
|
generateVariadic(f, i)
|
||||||
|
// unvariadic
|
||||||
|
generateUnvariadic(f, i)
|
||||||
|
// unsliced
|
||||||
|
generateUnsliced(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PipeCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "pipe",
|
||||||
|
Usage: "generate code for pipe, flow, curry, etc",
|
||||||
|
Description: "Code generation for pipe, flow, curry, etc",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generatePipeHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
164
v2/cli/reader.go
Normal file
164
v2/cli/reader.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateReaderFrom(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// From%d converts a function with %d parameters returning a [R] into a function with %d parameters returning a [Reader[C, R]]\n// The first parameter is considered to be the context [C] of the reader\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func From%d[F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", C, R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") Reader[C, R] {\n")
|
||||||
|
fmt.Fprintf(f, " return G.From%d[Reader[C, R]](f)\n", i)
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// From%d converts a function with %d parameters returning a [R] into a function with %d parameters returning a [GRA]\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(fg, "func From%d[GRA ~func(C) R, F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") R")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", C, R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA {\n")
|
||||||
|
fmt.Fprintf(fg, " return MakeReader[GRA](func(r C) R {\n")
|
||||||
|
fmt.Fprintf(fg, " return f(r")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")\n")
|
||||||
|
fmt.Fprintf(fg, " })\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateReaderHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// construct subdirectory
|
||||||
|
genFilename := filepath.Join("generic", filename)
|
||||||
|
err = os.MkdirAll("generic", os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fg, err := os.Create(filepath.Clean(genFilename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fg.Close()
|
||||||
|
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/%s/generic"
|
||||||
|
)
|
||||||
|
`, pkg)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(fg, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(fg, "// %s\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, "package generic\n\n")
|
||||||
|
|
||||||
|
// from
|
||||||
|
generateReaderFrom(f, fg, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// from
|
||||||
|
generateReaderFrom(f, fg, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReaderCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "reader",
|
||||||
|
Usage: "generate code for Reader",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateReaderHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
294
v2/cli/readerioeither.go
Normal file
294
v2/cli/readerioeither.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateReaderIOEitherFrom(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// From%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[R]]\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func From%d[F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func() (R, error)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", C, R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[C, error, R] {\n")
|
||||||
|
fmt.Fprintf(f, " return G.From%d[ReaderIOEither[C, error, R]](f)\n", i)
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// From%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The first parameter is considerd to be the context [C].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(fg, "func From%d[GRA ~func(C) GIOA, F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") func() (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", C, R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return RD.From%d[GRA](func(r C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GIOA {\n")
|
||||||
|
fmt.Fprintf(fg, " return E.Eitherize0(f(r")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "))\n")
|
||||||
|
fmt.Fprintf(fg, " })\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateReaderIOEitherEitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[C, error, R]]\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Eitherize%d[F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error)")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", C, R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[C, error, R] {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Eitherize%d[ReaderIOEither[C, error, R]](f)\n", i)
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The first parameter is considered to be the context [C].\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Eitherize%d[GRA ~func(C) GIOA, F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", C, R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return From%d[GRA](func(r C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") func() (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return func() (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return f(r")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")\n")
|
||||||
|
fmt.Fprintf(fg, " }})\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateReaderIOEitherUneitherize(f, fg *os.File, i int) {
|
||||||
|
// non generic version
|
||||||
|
fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a [ReaderIOEither[C, error, R]] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the context [C].\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ReaderIOEither[C, error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", C, R any](f F) func(C")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return G.Uneitherize%d[ReaderIOEither[C, error, R]", i)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, ", func(C")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")(R, error)](f)\n")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
|
||||||
|
// generic version
|
||||||
|
fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a [GRA] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the context [C].\n", i, i, i)
|
||||||
|
fmt.Fprintf(fg, "func Uneitherize%d[GRA ~func(C) GIOA, F ~func(C", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ", C, R any](f func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") GRA) F {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, " return func(c C")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(fg, ", t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ") (R, error) {\n")
|
||||||
|
fmt.Fprintf(fg, " return E.UnwrapError(f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(fg, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(fg, ")(c)())\n")
|
||||||
|
fmt.Fprintf(fg, " }\n")
|
||||||
|
fmt.Fprintf(fg, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateReaderIOEitherHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// construct subdirectory
|
||||||
|
genFilename := filepath.Join("generic", filename)
|
||||||
|
err = os.MkdirAll("generic", os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fg, err := os.Create(filepath.Clean(genFilename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fg.Close()
|
||||||
|
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
G "github.com/IBM/fp-go/v2/%s/generic"
|
||||||
|
)
|
||||||
|
`, pkg)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(fg, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(fg, "// %s\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, "package generic\n\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(fg, `
|
||||||
|
import (
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
RD "github.com/IBM/fp-go/v2/reader/generic"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// from
|
||||||
|
generateReaderIOEitherFrom(f, fg, 0)
|
||||||
|
// eitherize
|
||||||
|
generateReaderIOEitherEitherize(f, fg, 0)
|
||||||
|
// uneitherize
|
||||||
|
generateReaderIOEitherUneitherize(f, fg, 0)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// from
|
||||||
|
generateReaderIOEitherFrom(f, fg, i)
|
||||||
|
// eitherize
|
||||||
|
generateReaderIOEitherEitherize(f, fg, i)
|
||||||
|
// uneitherize
|
||||||
|
generateReaderIOEitherUneitherize(f, fg, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReaderIOEitherCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "readerioeither",
|
||||||
|
Usage: "generate code for ReaderIOEither",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateReaderIOEitherHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
15
v2/cli/templates/functions.go
Normal file
15
v2/cli/templates/functions.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
templateFunctions = template.FuncMap{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Parse(name, tmpl string) E.Either[error, *template.Template] {
|
||||||
|
return E.TryCatchError(template.New(name).Funcs(templateFunctions).Parse(tmpl))
|
||||||
|
}
|
||||||
625
v2/cli/tuple.go
Normal file
625
v2/cli/tuple.go
Normal file
@@ -0,0 +1,625 @@
|
|||||||
|
// 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 cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeTupleType(f *os.File, symbol string, i int) {
|
||||||
|
fmt.Fprintf(f, "Tuple%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s%d", symbol, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTupleType(name string) func(i int) string {
|
||||||
|
return func(i int) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.WriteString(fmt.Sprintf("Tuple%d[", i))
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s%d", name, j+1))
|
||||||
|
}
|
||||||
|
buf.WriteString("]")
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePush(f *os.File, i int) {
|
||||||
|
tuple1 := makeTupleType("T")(i)
|
||||||
|
tuple2 := makeTupleType("T")(i + 1)
|
||||||
|
// Create the replicate version
|
||||||
|
fmt.Fprintf(f, "\n// Push%d creates a [Tuple%d] from a [Tuple%d] by appending a constant value\n", i, i+1, i)
|
||||||
|
fmt.Fprintf(f, "func Push%d[", i)
|
||||||
|
// function prototypes
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](value T%d) func(%s) %s {\n", i+1, tuple1, tuple2)
|
||||||
|
fmt.Fprintf(f, " return func(t %s) %s {\n", tuple1, tuple2)
|
||||||
|
fmt.Fprintf(f, " return MakeTuple%d(", i+1)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t.F%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", value)\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateReplicate(f *os.File, i int) {
|
||||||
|
// Create the replicate version
|
||||||
|
fmt.Fprintf(f, "\n// Replicate%d creates a [Tuple%d] with all fields set to the input value `t`\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func Replicate%d[T any](t T) Tuple%d[", i, i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "] {\n")
|
||||||
|
// execute the mapping
|
||||||
|
fmt.Fprintf(f, " return MakeTuple%d(", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMap(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Map%d maps each value of a [Tuple%d] via a mapping function\n", i, i)
|
||||||
|
fmt.Fprintf(f, "func Map%d[", i)
|
||||||
|
// function prototypes
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F%d ~func(T%d) R%d", j, j, j)
|
||||||
|
}
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d, R%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d F%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") ")
|
||||||
|
writeTupleType(f, "R", i)
|
||||||
|
fmt.Fprintf(f, " {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return func(t ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") ")
|
||||||
|
writeTupleType(f, "R", i)
|
||||||
|
fmt.Fprintf(f, " {\n")
|
||||||
|
|
||||||
|
// execute the mapping
|
||||||
|
fmt.Fprintf(f, " return MakeTuple%d(\n", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " f%d(t.F%d),\n", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMonoid(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Monoid%d creates a [Monoid] for a [Tuple%d] based on %d monoids for the contained types\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Monoid%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "m%d M.Monoid[T%d]", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") M.Monoid[")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "] {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return M.MakeMonoid(func(l, r ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "{\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return MakeTuple%d(", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "m%d.Concat(l.F%d, r.F%d)", j, j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, " }, MakeTuple%d(", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "m%d.Empty()", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "))\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOrd(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Ord%d creates n [Ord] for a [Tuple%d] based on %d [Ord]s for the contained types\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Ord%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "o%d O.Ord[T%d]", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") O.Ord[")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, "] {\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, " return O.MakeOrd(func(l, r ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") int {\n")
|
||||||
|
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " if c:= o%d.Compare(l.F%d, r.F%d); c != 0 {return c}\n", j, j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " return 0\n")
|
||||||
|
fmt.Fprintf(f, " }, func(l, r ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") bool {\n")
|
||||||
|
fmt.Fprintf(f, " return ")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, " && ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "o%d.Equals(l.F%d, r.F%d)", j, j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "\n")
|
||||||
|
fmt.Fprintf(f, " })\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupleType(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Tuple%d is a struct that carries %d independently typed values\n", i, i)
|
||||||
|
fmt.Fprintf(f, "type Tuple%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any] struct {\n")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " F%d T%d\n", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMakeTupleType(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// MakeTuple%d is a function that converts its %d parameters into a [Tuple%d]\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func MakeTuple%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, " {\n")
|
||||||
|
fmt.Fprintf(f, " return Tuple%d[", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]{")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUntupled(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Untupled%d converts a function with a [Tuple%d] parameter into a function with %d parameters\n// The inverse function is [Tupled%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Untupled%d[F ~func(Tuple%d[", i, i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) R")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d T%d", j+1, j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R {\n")
|
||||||
|
fmt.Fprintf(f, " return f(MakeTuple%d(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(f, "))")
|
||||||
|
fmt.Fprintln(f, " }")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupled(f *os.File, i int) {
|
||||||
|
// Create the optionize version
|
||||||
|
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
|
||||||
|
fmt.Fprintf(f, "func Tupled%d[F ~func(", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") R")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](f F) func(Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(t Tuple%d[", i)
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "T%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "]) R {\n")
|
||||||
|
fmt.Fprintf(f, " return f(")
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t.F%d", j+1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintln(f, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupleHelpers(filename string, count int) error {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkg := filepath.Base(absDir)
|
||||||
|
f, err := os.Create(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// log
|
||||||
|
log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count)
|
||||||
|
|
||||||
|
// some header
|
||||||
|
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||||
|
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||||
|
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||||
|
|
||||||
|
fmt.Fprintf(f, `
|
||||||
|
import (
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
O "github.com/IBM/fp-go/v2/ord"
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// tuple type
|
||||||
|
generateTupleType(f, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
// tuple generator
|
||||||
|
generateMakeTupleType(f, i)
|
||||||
|
// tupled wrapper
|
||||||
|
generateTupled(f, i)
|
||||||
|
// untupled wrapper
|
||||||
|
generateUntupled(f, i)
|
||||||
|
// monoid
|
||||||
|
generateMonoid(f, i)
|
||||||
|
// generate order
|
||||||
|
generateOrd(f, i)
|
||||||
|
// generate map
|
||||||
|
generateMap(f, i)
|
||||||
|
// generate replicate
|
||||||
|
generateReplicate(f, i)
|
||||||
|
// generate tuple functions such as string and fmt
|
||||||
|
generateTupleString(f, i)
|
||||||
|
// generate json support
|
||||||
|
generateTupleMarshal(f, i)
|
||||||
|
// generate json support
|
||||||
|
generateTupleUnmarshal(f, i)
|
||||||
|
// generate toArray
|
||||||
|
generateToArray(f, i)
|
||||||
|
// generate fromArray
|
||||||
|
generateFromArray(f, i)
|
||||||
|
// generate push
|
||||||
|
if i < count {
|
||||||
|
generatePush(f, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupleMarshal(f *os.File, i int) {
|
||||||
|
// Create the stringify version
|
||||||
|
fmt.Fprintf(f, "\n// MarshalJSON marshals the [Tuple%d] into a JSON array\n", i)
|
||||||
|
fmt.Fprintf(f, "func (t ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
|
||||||
|
fmt.Fprintf(f, " return tupleMarshalJSON(")
|
||||||
|
// function prototypes
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t.F%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupleUnmarshal(f *os.File, i int) {
|
||||||
|
// Create the stringify version
|
||||||
|
fmt.Fprintf(f, "\n// UnmarshalJSON unmarshals a JSON array into a [Tuple%d]\n", i)
|
||||||
|
fmt.Fprintf(f, "func (t *")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n")
|
||||||
|
fmt.Fprintf(f, " return tupleUnmarshalJSON(data")
|
||||||
|
// function prototypes
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", &t.F%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateToArray(f *os.File, i int) {
|
||||||
|
// Create the stringify version
|
||||||
|
fmt.Fprintf(f, "\n// ToArray converts the [Tuple%d] into an array of type [R] using %d transformation functions from [T] to [R]\n// The inverse function is [FromArray%d]\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func ToArray%d[", i)
|
||||||
|
// function prototypes
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F%d ~func(T%d) R", j, j)
|
||||||
|
}
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d F%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(t ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") []R {\n")
|
||||||
|
fmt.Fprintf(f, " return func(t ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") []R {\n")
|
||||||
|
fmt.Fprintf(f, " return []R{\n")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " f%d(t.F%d),\n", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFromArray(f *os.File, i int) {
|
||||||
|
// Create the stringify version
|
||||||
|
fmt.Fprintf(f, "\n// FromArray converts an array of [R] into a [Tuple%d] using %d functions from [R] to [T]\n// The inverse function is [ToArray%d]\n", i, i, i)
|
||||||
|
fmt.Fprintf(f, "func FromArray%d[", i)
|
||||||
|
// function prototypes
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "F%d ~func(R) T%d", j, j)
|
||||||
|
}
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, ", T%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ", R any](")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "f%d F%d", j, j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ") func(r []R) ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, " {\n")
|
||||||
|
fmt.Fprintf(f, " return func(r []R) ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, " {\n")
|
||||||
|
fmt.Fprintf(f, " return MakeTuple%d(\n", i)
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
fmt.Fprintf(f, " f%d(r[%d]),\n", j, j-1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, " )\n")
|
||||||
|
fmt.Fprintf(f, " }\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTupleString(f *os.File, i int) {
|
||||||
|
// Create the stringify version
|
||||||
|
fmt.Fprintf(f, "\n// String prints some debug info for the [Tuple%d]\n", i)
|
||||||
|
fmt.Fprintf(f, "func (t ")
|
||||||
|
writeTupleType(f, "T", i)
|
||||||
|
fmt.Fprintf(f, ") String() string {\n")
|
||||||
|
// convert to string
|
||||||
|
fmt.Fprint(f, " return tupleString(")
|
||||||
|
for j := 1; j <= i; j++ {
|
||||||
|
if j > 1 {
|
||||||
|
fmt.Fprintf(f, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "t.F%d", j)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, ")\n")
|
||||||
|
fmt.Fprintf(f, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func generateTupleJson(f *os.File, i int) {
|
||||||
|
// // Create the stringify version
|
||||||
|
// fmt.Fprintf(f, "\n// MarshalJSON converts the [Tuple%d] into a JSON byte stream\n", i)
|
||||||
|
// fmt.Fprintf(f, "func (t ")
|
||||||
|
// writeTupleType(f, "T", i)
|
||||||
|
// fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
|
||||||
|
// // convert to string
|
||||||
|
// fmt.Fprintf(f, " return fmt.Sprintf(\"Tuple%d[", i)
|
||||||
|
// for j := 1; j <= i; j++ {
|
||||||
|
// if j > 1 {
|
||||||
|
// fmt.Fprintf(f, ", ")
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(f, "%s", "%T")
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(f, "](")
|
||||||
|
// for j := 1; j <= i; j++ {
|
||||||
|
// if j > 1 {
|
||||||
|
// fmt.Fprintf(f, ", ")
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(f, "%s", "%v")
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(f, ")\", ")
|
||||||
|
// for j := 1; j <= i; j++ {
|
||||||
|
// if j > 1 {
|
||||||
|
// fmt.Fprintf(f, ", ")
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(f, "t.F%d", j)
|
||||||
|
// }
|
||||||
|
// for j := 1; j <= i; j++ {
|
||||||
|
// fmt.Fprintf(f, ", t.F%d", j)
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(f, ")\n")
|
||||||
|
// fmt.Fprintf(f, "}\n")
|
||||||
|
// }
|
||||||
|
|
||||||
|
func TupleCommand() *C.Command {
|
||||||
|
return &C.Command{
|
||||||
|
Name: "tuple",
|
||||||
|
Usage: "generate code for Tuple",
|
||||||
|
Flags: []C.Flag{
|
||||||
|
flagCount,
|
||||||
|
flagFilename,
|
||||||
|
},
|
||||||
|
Action: func(ctx *C.Context) error {
|
||||||
|
return generateTupleHelpers(
|
||||||
|
ctx.String(keyFilename),
|
||||||
|
ctx.Int(keyCount),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
59
v2/constant/const.go
Normal file
59
v2/constant/const.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// 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 constant
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
S "github.com/IBM/fp-go/v2/semigroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Const[E, A any] struct {
|
||||||
|
value E
|
||||||
|
}
|
||||||
|
|
||||||
|
func Make[E, A any](e E) Const[E, A] {
|
||||||
|
return Const[E, A]{value: e}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unwrap[E, A any](c Const[E, A]) E {
|
||||||
|
return c.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] {
|
||||||
|
return F.Constant1[A](Make[E, A](m.Empty()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[E, A, B any](fa Const[E, A], _ func(A) B) Const[E, B] {
|
||||||
|
return Make[E, B](fa.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[E, A, B any](s S.Semigroup[E]) func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
||||||
|
return func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] {
|
||||||
|
return Make[E, B](s.Concat(fab.value, fa.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[E, A, B any](f func(A) B) func(fa Const[E, A]) Const[E, B] {
|
||||||
|
return F.Bind2nd(MonadMap[E, A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[E, A, B any](s S.Semigroup[E]) func(fa Const[E, A]) func(fab Const[E, func(A) B]) Const[E, B] {
|
||||||
|
monadap := MonadAp[E, A, B](s)
|
||||||
|
return func(fa Const[E, A]) func(fab Const[E, func(A) B]) Const[E, B] {
|
||||||
|
return F.Bind2nd(monadap, fa)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
v2/constant/const_test.go
Normal file
40
v2/constant/const_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// 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 constant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
S "github.com/IBM/fp-go/v2/string"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
fa := Make[string, int]("foo")
|
||||||
|
assert.Equal(t, fa, F.Pipe1(fa, Map[string, int](utils.Double)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOf(t *testing.T) {
|
||||||
|
assert.Equal(t, Make[string, int](""), Of[string, int](S.Monoid)(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAp(t *testing.T) {
|
||||||
|
fab := Make[string, int]("bar")
|
||||||
|
assert.Equal(t, Make[string, int]("foobar"), Ap[string, int, int](S.Monoid)(fab)(Make[string, func(int) int]("foo")))
|
||||||
|
}
|
||||||
40
v2/constraints/constraints.go
Normal file
40
v2/constraints/constraints.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// 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 constraints
|
||||||
|
|
||||||
|
type Ordered interface {
|
||||||
|
Integer | Float | ~string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Signed interface {
|
||||||
|
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Unsigned interface {
|
||||||
|
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type Integer interface {
|
||||||
|
Signed | Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float interface {
|
||||||
|
~float32 | ~float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Complex interface {
|
||||||
|
~complex64 | ~complex128
|
||||||
|
}
|
||||||
17
v2/context/doc.go
Normal file
17
v2/context/doc.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// 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 context contains versions of reader IO monads that work with a golang [context.Context]
|
||||||
|
package context
|
||||||
33
v2/context/ioeither/generic/ioeither.go
Normal file
33
v2/context/ioeither/generic/ioeither.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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 generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
IOE "github.com/IBM/fp-go/v2/ioeither/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||||
|
func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO {
|
||||||
|
return IOE.MakeIO[GIO](func() E.Either[error, A] {
|
||||||
|
if err := context.Cause(ctx); err != nil {
|
||||||
|
return E.Left[A](err)
|
||||||
|
}
|
||||||
|
return ma()
|
||||||
|
})
|
||||||
|
}
|
||||||
33
v2/context/ioeither/ioeither.go
Normal file
33
v2/context/ioeither/ioeither.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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 ioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/either"
|
||||||
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||||
|
func WithContext[A any](ctx context.Context, ma IOE.IOEither[error, A]) IOE.IOEither[error, A] {
|
||||||
|
return func() either.Either[error, A] {
|
||||||
|
if err := context.Cause(ctx); err != nil {
|
||||||
|
return either.Left[A](err)
|
||||||
|
}
|
||||||
|
return ma()
|
||||||
|
}
|
||||||
|
}
|
||||||
33
v2/context/readereither/array.go
Normal file
33
v2/context/readereither/array.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import "github.com/IBM/fp-go/v2/readereither"
|
||||||
|
|
||||||
|
// TraverseArray transforms an array
|
||||||
|
func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
||||||
|
return readereither.TraverseArray(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndex transforms an array
|
||||||
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
||||||
|
return readereither.TraverseArrayWithIndex(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||||
|
func SequenceArray[A any](ma []ReaderEither[A]) ReaderEither[[]A] {
|
||||||
|
return readereither.SequenceArray(ma)
|
||||||
|
}
|
||||||
68
v2/context/readereither/bind.go
Normal file
68
v2/context/readereither/bind.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
G "github.com/IBM/fp-go/v2/readereither/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) ReaderEither[S] {
|
||||||
|
return G.Do[ReaderEither[S], context.Context, error, S](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) ReaderEither[T],
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(ReaderEither[T]) ReaderEither[S1] {
|
||||||
|
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderEither[T],
|
||||||
|
) func(ReaderEither[S1]) ReaderEither[S2] {
|
||||||
|
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
|
||||||
|
}
|
||||||
58
v2/context/readereither/bind_test.go
Normal file
58
v2/context/readereither/bind_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) ReaderEither[string] {
|
||||||
|
return Of("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) ReaderEither[string] {
|
||||||
|
return Of("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background()), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
32
v2/context/readereither/cancel.go
Normal file
32
v2/context/readereither/cancel.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
)
|
||||||
|
|
||||||
|
// withContext wraps an existing ReaderEither and performs a context check for cancellation before deletating
|
||||||
|
func WithContext[A any](ma ReaderEither[A]) ReaderEither[A] {
|
||||||
|
return func(ctx context.Context) E.Either[error, A] {
|
||||||
|
if err := context.Cause(ctx); err != nil {
|
||||||
|
return E.Left[A](err)
|
||||||
|
}
|
||||||
|
return ma(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
53
v2/context/readereither/curry.go
Normal file
53
v2/context/readereither/curry.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||||
|
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||||
|
|
||||||
|
func Curry0[A any](f func(context.Context) (A, error)) ReaderEither[A] {
|
||||||
|
return readereither.Curry0(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Curry1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
|
||||||
|
return readereither.Curry1(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[A] {
|
||||||
|
return readereither.Curry2(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[A] {
|
||||||
|
return readereither.Curry3(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Uncurry1[T1, A any](f func(T1) ReaderEither[A]) func(context.Context, T1) (A, error) {
|
||||||
|
return readereither.Uncurry1(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Uncurry2[T1, T2, A any](f func(T1) func(T2) ReaderEither[A]) func(context.Context, T1, T2) (A, error) {
|
||||||
|
return readereither.Uncurry2(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[A]) func(context.Context, T1, T2, T3) (A, error) {
|
||||||
|
return readereither.Uncurry3(f)
|
||||||
|
}
|
||||||
39
v2/context/readereither/exec/exec.go
Normal file
39
v2/context/readereither/exec/exec.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// 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 exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/context/readereither"
|
||||||
|
"github.com/IBM/fp-go/v2/either"
|
||||||
|
"github.com/IBM/fp-go/v2/exec"
|
||||||
|
"github.com/IBM/fp-go/v2/function"
|
||||||
|
INTE "github.com/IBM/fp-go/v2/internal/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Command executes a command
|
||||||
|
// use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input
|
||||||
|
// typically you'd rather use the ReaderIOEither version of the command
|
||||||
|
Command = function.Curry3(command)
|
||||||
|
)
|
||||||
|
|
||||||
|
func command(name string, args []string, in []byte) readereither.ReaderEither[exec.CommandOutput] {
|
||||||
|
return func(ctx context.Context) either.Either[error, exec.CommandOutput] {
|
||||||
|
return either.TryCatchError(INTE.Exec(ctx, name, args, in))
|
||||||
|
}
|
||||||
|
}
|
||||||
41
v2/context/readereither/from.go
Normal file
41
v2/context/readereither/from.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||||
|
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||||
|
|
||||||
|
func From0[A any](f func(context.Context) (A, error)) func() ReaderEither[A] {
|
||||||
|
return readereither.From0(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func From1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
|
||||||
|
return readereither.From1(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func From2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1, T2) ReaderEither[A] {
|
||||||
|
return readereither.From2(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderEither[A] {
|
||||||
|
return readereither.From3(f)
|
||||||
|
}
|
||||||
94
v2/context/readereither/reader.go
Normal file
94
v2/context/readereither/reader.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FromEither[A any](e Either[A]) ReaderEither[A] {
|
||||||
|
return readereither.FromEither[context.Context](e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Left[A any](l error) ReaderEither[A] {
|
||||||
|
return readereither.Left[context.Context, A](l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Right[A any](r A) ReaderEither[A] {
|
||||||
|
return readereither.Right[context.Context, error](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
||||||
|
return readereither.MonadMap(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[A, B any](f func(A) B) Operator[A, B] {
|
||||||
|
return readereither.Map[context.Context, error](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChain[A, B any](ma ReaderEither[A], f func(A) ReaderEither[B]) ReaderEither[B] {
|
||||||
|
return readereither.MonadChain(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chain[A, B any](f func(A) ReaderEither[B]) Operator[A, B] {
|
||||||
|
return readereither.Chain(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Of[A any](a A) ReaderEither[A] {
|
||||||
|
return readereither.Of[context.Context, error](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadAp[A, B any](fab ReaderEither[func(A) B], fa ReaderEither[A]) ReaderEither[B] {
|
||||||
|
return readereither.MonadAp(fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ap[A, B any](fa ReaderEither[A]) func(ReaderEither[func(A) B]) ReaderEither[B] {
|
||||||
|
return readereither.Ap[B](fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderEither[A] {
|
||||||
|
return readereither.FromPredicate[context.Context](pred, onFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
||||||
|
return readereither.OrElse(onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ask() ReaderEither[context.Context] {
|
||||||
|
return readereither.Ask[context.Context, error]()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) Either[B]) ReaderEither[B] {
|
||||||
|
return readereither.MonadChainEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainEitherK[A, B any](f func(A) Either[B]) func(ma ReaderEither[A]) ReaderEither[B] {
|
||||||
|
return readereither.ChainEitherK[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operator[A, B] {
|
||||||
|
return readereither.ChainOptionK[context.Context, A, B](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MonadFlap[B, A any](fab ReaderEither[func(A) B], a A) ReaderEither[B] {
|
||||||
|
return readereither.MonadFlap(fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||||
|
return readereither.Flap[context.Context, error, B](a)
|
||||||
|
}
|
||||||
39
v2/context/readereither/sequence.go
Normal file
39
v2/context/readereither/sequence.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// 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 readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
|
"github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
||||||
|
|
||||||
|
func SequenceT1[A any](a ReaderEither[A]) ReaderEither[tuple.Tuple1[A]] {
|
||||||
|
return readereither.SequenceT1(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT2[A, B any](a ReaderEither[A], b ReaderEither[B]) ReaderEither[tuple.Tuple2[A, B]] {
|
||||||
|
return readereither.SequenceT2(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT3[A, B, C any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C]) ReaderEither[tuple.Tuple3[A, B, C]] {
|
||||||
|
return readereither.SequenceT3(a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SequenceT4[A, B, C, D any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C], d ReaderEither[D]) ReaderEither[tuple.Tuple4[A, B, C, D]] {
|
||||||
|
return readereither.SequenceT4(a, b, c, d)
|
||||||
|
}
|
||||||
35
v2/context/readereither/type.go
Normal file
35
v2/context/readereither/type.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// 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 readereither implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
|
||||||
|
package readereither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/either"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Option[A any] = option.Option[A]
|
||||||
|
Either[A any] = either.Either[error, A]
|
||||||
|
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
|
||||||
|
ReaderEither[A any] = readereither.ReaderEither[context.Context, error, A]
|
||||||
|
|
||||||
|
Operator[A, B any] = reader.Reader[ReaderEither[A], ReaderEither[B]]
|
||||||
|
)
|
||||||
89
v2/context/readerioeither/bind.go
Normal file
89
v2/context/readerioeither/bind.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// 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 readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/chain"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bind creates an empty context of type [S] to be used with the [Bind] operation
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) ReaderIOEither[S] {
|
||||||
|
return Of(empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Bind[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) ReaderIOEither[T],
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return chain.Bind(
|
||||||
|
Chain[S1, S2],
|
||||||
|
Map[T, S2],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
func Let[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return functor.Let(
|
||||||
|
Map[S1, S2],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
func LetTo[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return functor.LetTo(
|
||||||
|
Map[S1, S2],
|
||||||
|
setter,
|
||||||
|
b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
func BindTo[S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) Operator[T, S1] {
|
||||||
|
return chain.BindTo(
|
||||||
|
Map[T, S1],
|
||||||
|
setter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderIOEither[T],
|
||||||
|
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
||||||
|
return apply.ApS(
|
||||||
|
Ap[S2, T],
|
||||||
|
Map[S1, func(T) S2],
|
||||||
|
setter,
|
||||||
|
fa,
|
||||||
|
)
|
||||||
|
}
|
||||||
58
v2/context/readerioeither/bind_test.go
Normal file
58
v2/context/readerioeither/bind_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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 readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) ReaderIOEither[string] {
|
||||||
|
return Of("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) ReaderIOEither[string] {
|
||||||
|
return Of("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
44
v2/context/readerioeither/bracket.go
Normal file
44
v2/context/readerioeither/bracket.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// 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 readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/internal/bracket"
|
||||||
|
"github.com/IBM/fp-go/v2/readerio"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||||
|
// whether the body action returns and error or not.
|
||||||
|
func Bracket[
|
||||||
|
A, B, ANY any](
|
||||||
|
|
||||||
|
acquire ReaderIOEither[A],
|
||||||
|
use func(A) ReaderIOEither[B],
|
||||||
|
release func(A, Either[B]) ReaderIOEither[ANY],
|
||||||
|
) ReaderIOEither[B] {
|
||||||
|
return bracket.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY], Either[B], A, B](
|
||||||
|
readerio.Of[context.Context, Either[B]],
|
||||||
|
MonadChain[A, B],
|
||||||
|
readerio.MonadChain[context.Context, Either[B], Either[B]],
|
||||||
|
MonadChain[ANY, B],
|
||||||
|
|
||||||
|
acquire,
|
||||||
|
use,
|
||||||
|
release,
|
||||||
|
)
|
||||||
|
}
|
||||||
42
v2/context/readerioeither/cancel.go
Normal file
42
v2/context/readerioeither/cancel.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// 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 readerioeither
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
CIOE "github.com/IBM/fp-go/v2/context/ioeither"
|
||||||
|
"github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithContext wraps an existing [ReaderIOEither] and performs a context check for cancellation before delegating.
|
||||||
|
// This ensures that if the context is already canceled, the computation short-circuits immediately
|
||||||
|
// without executing the wrapped computation.
|
||||||
|
//
|
||||||
|
// This is useful for adding cancellation awareness to computations that might not check the context themselves.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The ReaderIOEither to wrap with context checking
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOEither that checks for cancellation before executing.
|
||||||
|
func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
|
return func(ctx context.Context) IOEither[A] {
|
||||||
|
if err := context.Cause(ctx); err != nil {
|
||||||
|
return ioeither.Left[A](err)
|
||||||
|
}
|
||||||
|
return CIOE.WithContext(ctx, ma(ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user