1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-24 19:29:11 +02:00

Compare commits

...

41 Commits

Author SHA1 Message Date
Dr. Carsten Leue
e2da181297 fix: adjust Pair to Haskell semantic
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-10 00:06:07 +01:00
Dr. Carsten Leue
24960f87a0 fix: some cleanup
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-08 15:25:32 +01:00
Dr. Carsten Leue
6cffae8f89 fix: simplify
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-08 00:00:43 +01:00
Dr. Carsten Leue
4fa30b60c0 fix: reformat
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-06 19:08:59 +01:00
Dr. Carsten Leue
6b03e19c67 fix: cleanup
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-06 18:18:44 +01:00
Dr. Carsten Leue
3be4a9ee9d fix: new build script
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-06 18:17:29 +01:00
Dr. Carsten Leue
b553ff8d9d fix: convert a bit of reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-05 17:30:23 +01:00
Dr. Carsten Leue
5bb85ecca7 fix: convert reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-05 11:24:14 +01:00
Dr. Carsten Leue
2d2f4fa325 fix: convert iooption
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-05 10:38:29 +01:00
Dr. Carsten Leue
e7e58c9d54 fix: get rid of more IO 2025-03-05 00:04:52 +01:00
Dr. Carsten Leue
e20679d73c fix: get rid of more IO methods
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-04 23:45:48 +01:00
Dr. Carsten Leue
b8013af7fa fix: adjust IO generation code
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-04 23:28:08 +01:00
Dr. Carsten Leue
4d008e636a fix: next step in migration
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-04 21:59:09 +01:00
Dr. Carsten Leue
8a4ecd2203 fix: refactorY
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-03 12:24:35 +01:00
Dr. Carsten Leue
24a7cae6e9 fix: migrate ioeither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-03 11:44:51 +01:00
Dr. Carsten Leue
c641a1ae7c fix: complete migration of IO
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-02 23:41:57 +01:00
Dr. Carsten Leue
d6766c5439 fix: migrate traversal
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-02 13:07:12 +01:00
Dr. Carsten Leue
b53ae32570 fix: migrate MonadTraverseArray and TraverseArray
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-02 11:08:09 +01:00
Dr. Carsten Leue
eb4218c575 fix: slowly migrate IO
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-02 10:57:52 +01:00
Dr. Carsten Leue
ddb9e48441 fix: initial checkin of v2
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-03-01 23:07:56 +01:00
renovate[bot]
7a3989989b chore(deps): update actions/setup-node action to v4.2.0 (#127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 22:04:25 +00:00
renovate[bot]
6a6d53f025 fix(deps): update module github.com/stretchr/testify to v1.10.0 (#126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-23 15:33:28 +00:00
renovate[bot]
078da752cd chore(deps): update actions/setup-node action to v4.1.0 2024-10-24 20:12:43 +00:00
renovate[bot]
1a489fde27 chore(deps): update actions/checkout action to v4.2.2 2024-10-23 19:31:36 +00:00
renovate[bot]
a135b2acae fix(deps): update module github.com/urfave/cli/v2 to v2.27.5 2024-10-13 20:09:16 +00:00
renovate[bot]
9e9dfa1f5f chore(deps): update actions/checkout action to v4.2.1 2024-10-07 22:39:01 +00:00
renovate[bot]
dd87ea12b3 chore(deps): update actions/checkout action to v4.2.0 2024-09-25 21:29:08 +00:00
renovate[bot]
5fc0d18c97 chore(deps): update actions/setup-node action to v4.0.4 2024-09-19 20:14:45 +00:00
CRaLFa
76c1297576 fix typos in README and comment (#119)
Signed-off-by: CRaLFa <minami.smd@gmail.com>
2024-09-12 09:40:19 +02:00
Dr. Carsten Leue
68aeb4c725 fix: script
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-08-27 16:57:59 +02:00
Dr. Carsten Leue
53f3fa1828 fix: semantic release
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-08-27 16:43:21 +02:00
Dr. Carsten Leue
97e1e4d92d fix: add test for go 1.23
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-08-27 16:39:52 +02:00
Dr. Carsten Leue
ec57d5cd4a fix: go mod tidy
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-08-27 16:37:22 +02:00
renovate[bot]
0ae5b43724 fix(deps): update module github.com/urfave/cli/v2 to v2.27.4 2024-08-11 19:12:08 +00:00
renovate[bot]
e73e14c0ae fix(deps): update module github.com/urfave/cli/v2 to v2.27.3 2024-07-25 03:34:59 +00:00
renovate[bot]
325bc376f9 chore(deps): update actions/setup-node action to v4.0.3 2024-07-09 20:06:20 +00:00
Dr. Carsten Leue
f646ace9fe chore: git mod tidy
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-06-24 09:24:15 +02:00
Laglangyue
9f8161fbc1 chore: ignore fp-go file for unix machine (#115)
Signed-off-by: tangjiafu <tangjiafu@apache.org>
Co-authored-by: tangjiafu <tangjiafu@apache.org>
2024-06-24 09:22:40 +02:00
renovate[bot]
1c4f2c0403 chore(deps): update actions/checkout action to v4.1.7 2024-06-13 02:39:23 +00:00
Dr. Carsten Leue
3b3b80aed0 fix: some more tests
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-05-27 11:54:04 +02:00
Dr. Carsten Leue
fdff4e4735 fix: more tests
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-05-26 16:23:40 +02:00
707 changed files with 67918 additions and 55 deletions

View File

@@ -4,9 +4,7 @@ on:
push:
branches:
- main
pull_request:
workflow_dispatch:
inputs:
dryRun:
@@ -15,35 +13,59 @@ on:
required: false
env:
# Currently no way to detect automatically
DEFAULT_BRANCH: main
GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
NODE_VERSION: 20
LATEST_GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
NODE_VERSION: 22
DRY_RUN: true
jobs:
build:
build-v1:
name: Build v1 (Go ${{ matrix.go-version }})
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x', '1.22.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:
# full checkout for semantic-release
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Set up go ${{ matrix.go-version }}
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
-
name: Tests
cache: true # Enable Go module caching
- name: Run tests
run: |
go mod tidy
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:
needs: [build]
name: Release
needs:
- build-v1
- build-v2
if: github.repository == 'IBM/fp-go' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 15
@@ -51,38 +73,37 @@ jobs:
contents: write
issues: write
pull-requests: write
steps:
# full checkout for semantic-release
- name: Full checkout
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Set up Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- name: Set up Node.js
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
with:
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
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: Check dry run
- name: Determine release mode
id: release-mode
run: |
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
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.dryRun }}" != "true" ]]; then
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
fi
- name: Semantic Release
- name: Run semantic release
run: |
npx -p "conventional-changelog-conventionalcommits@<8" -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:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
fp-go.exe
fp-go
main.exe
build/
.idea

View File

@@ -29,9 +29,9 @@ This library aims to provide a set of data types and functions that make it easy
### How does this play with the [🧘🏽 Zen Of Go](https://the-zen-of-go.netlify.app/)?
#### 🧘🏽 Each package fulfils a single purpose
#### 🧘🏽 Each package fulfills a single purpose
✔️ Each of the top level packages (e.g. Option, Either, ReaderIOEither, ...) fulfils the purpose of defining the respective data type and implementing the set of common operations for this data type.
✔️ Each of the top level packages (e.g. Option, Either, ReaderIOEither, ...) fulfills the purpose of defining the respective data type and implementing the set of common operations for this data type.
#### 🧘🏽 Handle errors explicitly

8
go.mod
View File

@@ -3,15 +3,15 @@ module github.com/IBM/fp-go
go 1.20
require (
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.27.2
github.com/stretchr/testify v1.10.0
github.com/urfave/cli/v2 v2.27.5
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

16
go.sum
View File

@@ -1,5 +1,7 @@
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -8,12 +10,14 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package file
package bracket
import (
F "github.com/IBM/fp-go/function"

View File

@@ -143,3 +143,41 @@ func SequenceArrayErrorTest[
}
}
}
// SequenceRecordTest tests if the sequence operation works in case the operation cannot error
func SequenceRecordTest[
HKTA,
HKTB,
HKTAA any, // HKT[map[string]string]
](
eq EQ.Eq[HKTB],
pa pointed.Pointed[string, HKTA],
pb pointed.Pointed[bool, HKTB],
faa functor.Functor[map[string]string, bool, HKTAA, HKTB],
seq func(map[string]HKTA) HKTAA,
) func(count int) func(t *testing.T) {
return func(count int) func(t *testing.T) {
exp := make(map[string]string)
good := make(map[string]HKTA)
for i := 0; i < count; i++ {
key := fmt.Sprintf("KeyData %d", i)
val := fmt.Sprintf("ValueData %d", i)
exp[key] = val
good[key] = pa.Of(val)
}
return func(t *testing.T) {
res := F.Pipe2(
good,
seq,
faa.Map(func(act map[string]string) bool {
return assert.Equal(t, exp, act)
}),
)
assert.True(t, eq.Equals(res, pb.Of(true)))
}
}
}

View File

@@ -16,21 +16,13 @@
package option
import (
"fmt"
"testing"
TST "github.com/IBM/fp-go/internal/testing"
"github.com/stretchr/testify/assert"
)
func TestSequenceRecord(t *testing.T) {
assert.Equal(t, Of(map[string]string{
"a": "A",
"b": "B",
}), SequenceRecord(map[string]Option[string]{
"a": Of("A"),
"b": Of("B"),
}))
}
func TestCompactRecord(t *testing.T) {
// make the map
m := make(map[string]Option[int])
@@ -45,3 +37,18 @@ func TestCompactRecord(t *testing.T) {
assert.Equal(t, exp, m1)
}
func TestSequenceRecord(t *testing.T) {
s := TST.SequenceRecordTest(
FromStrictEquals[bool](),
Pointed[string](),
Pointed[bool](),
Functor[map[string]string, bool](),
SequenceRecord[string, string],
)
for i := 0; i < 10; i++ {
t.Run(fmt.Sprintf("TestSequenceRecord %d", i), s(i))
}
}

View File

@@ -64,7 +64,7 @@ func Reverse[T any](o Ord[T]) Ord[T] {
}, o.Equals)
}
// Contramap creates an odering under a transformation function
// Contramap creates an ordering under a transformation function
func Contramap[A, B any](f func(B) A) func(Ord[A]) Ord[B] {
return func(o Ord[A]) Ord[B] {
return MakeOrd(func(x, y B) int {

View File

@@ -23,3 +23,8 @@ import (
func Eq[K comparable, V any](e E.Eq[V]) E.Eq[map[K]V] {
return G.Eq[map[K]V, K, V](e)
}
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[K, V comparable]() E.Eq[map[K]V] {
return G.FromStrictEquals[map[K]V]()
}

48
record/eq_test.go Normal file
View File

@@ -0,0 +1,48 @@
// Copyright (c) 2024 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 record
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFromStrictEquals(t *testing.T) {
m1 := map[string]string{
"a": "A",
"b": "B",
}
m2 := map[string]string{
"a": "A",
"b": "C",
}
m3 := map[string]string{
"a": "A",
"b": "B",
}
m4 := map[string]string{
"a": "A",
"b": "B",
"c": "C",
}
e := FromStrictEquals[string, string]()
assert.True(t, e.Equals(m1, m1))
assert.True(t, e.Equals(m1, m3))
assert.False(t, e.Equals(m1, m2))
assert.False(t, e.Equals(m1, m4))
}

View File

@@ -37,3 +37,8 @@ func Eq[M ~map[K]V, K comparable, V any](e E.Eq[V]) E.Eq[M] {
return equals(left, right, eq)
})
}
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[M ~map[K]V, K, V comparable]() E.Eq[M] {
return Eq[M](E.FromStrictEquals[V]())
}

View File

@@ -54,3 +54,69 @@ func TestUnionMonoid(t *testing.T) {
assert.Equal(t, res, m.Concat(x, y))
}
func TestUnionFirstMonoid(t *testing.T) {
m := UnionFirstMonoid[string, string]()
e := Empty[string, string]()
x := map[string]string{
"a": "a1",
"b": "b1",
"c": "c1",
}
y := map[string]string{
"b": "b2",
"c": "c2",
"d": "d2",
}
res := map[string]string{
"a": "a1",
"b": "b1",
"c": "c1",
"d": "d2",
}
assert.Equal(t, x, m.Concat(x, m.Empty()))
assert.Equal(t, x, m.Concat(m.Empty(), x))
assert.Equal(t, x, m.Concat(x, e))
assert.Equal(t, x, m.Concat(e, x))
assert.Equal(t, res, m.Concat(x, y))
}
func TestUnionLastMonoid(t *testing.T) {
m := UnionLastMonoid[string, string]()
e := Empty[string, string]()
x := map[string]string{
"a": "a1",
"b": "b1",
"c": "c1",
}
y := map[string]string{
"b": "b2",
"c": "c2",
"d": "d2",
}
res := map[string]string{
"a": "a1",
"b": "b2",
"c": "c2",
"d": "d2",
}
assert.Equal(t, x, m.Concat(x, m.Empty()))
assert.Equal(t, x, m.Concat(m.Empty(), x))
assert.Equal(t, x, m.Concat(x, e))
assert.Equal(t, x, m.Concat(e, x))
assert.Equal(t, res, m.Concat(x, y))
}

View File

@@ -176,3 +176,25 @@ func TestFromArrayMap(t *testing.T) {
"C": "C",
}, res2)
}
func TestEmpty(t *testing.T) {
nonEmpty := map[string]string{
"a": "A",
"b": "B",
}
empty := Empty[string, string]()
assert.True(t, IsEmpty(empty))
assert.False(t, IsEmpty(nonEmpty))
assert.False(t, IsNonEmpty(empty))
assert.True(t, IsNonEmpty(nonEmpty))
}
func TestHas(t *testing.T) {
nonEmpty := map[string]string{
"a": "A",
"b": "B",
}
assert.True(t, Has("a", nonEmpty))
assert.False(t, Has("c", nonEmpty))
}

43
v2/README.md Normal file
View 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

30
v2/array/any.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023 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
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
func Any[A any](pred func(A) bool) func([]A) bool {
return G.Any[[]A](pred)
}

30
v2/array/any_test.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023 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)))
}

351
v2/array/array.go Normal file
View File

@@ -0,0 +1,351 @@
// Copyright (c) 2023 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)
}
func MonadMap[A, B any](as []A, f func(a A) B) []B {
return G.MonadMap[[]A, []B](as, f)
}
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
}
func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
return G.MapWithIndex[[]A, []B](f)
}
func Map[A, B any](f func(a A) B) func([]A) []B {
return G.Map[[]A, []B, A, B](f)
}
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)
}
func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] {
return F.Bind2nd(filterRef[A], pred)
}
func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B {
return G.MonadFilterMap[[]A, []B](fa, f)
}
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)
}
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
}
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
return G.Reduce[[]A](f, initial)
}
func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B {
return G.ReduceWithIndex[[]A](f, initial)
}
func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B {
return G.ReduceRight[[]A](f, initial)
}
func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B {
return G.ReduceRightWithIndex[[]A](f, initial)
}
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)
}
}
func Append[A any](as []A, a A) []A {
return G.Append(as, a)
}
func IsEmpty[A any](as []A) bool {
return G.IsEmpty(as)
}
func IsNonEmpty[A any](as []A) bool {
return len(as) > 0
}
func Empty[A any]() []A {
return G.Empty[[]A]()
}
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)
}
func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
return G.MonadChain[[]A, []B](fa, f)
}
func Chain[A, B any](f func(A) []B) func([]A) []B {
return G.Chain[[]A, []B](f)
}
func MonadAp[B, A any](fab []func(A) B, fa []A) []B {
return G.MonadAp[[]B](fab, fa)
}
func Ap[B, A any](fa []A) func([]func(A) B) []B {
return G.Ap[[]B, []func(A) B](fa)
}
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
return G.Match[[]A](onEmpty, onNonEmpty)
}
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
}
func Tail[A any](as []A) O.Option[[]A] {
return G.Tail(as)
}
func Head[A any](as []A) O.Option[A] {
return G.Head(as)
}
func First[A any](as []A) O.Option[A] {
return G.First(as)
}
func Last[A any](as []A) O.Option[A] {
return G.Last(as)
}
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
}
}
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:]
}
}
func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
concatAll := ConcatAll[A](m)
return func(middle A) func([]A) A {
return Match(m.Empty, F.Flow2(Intersperse(middle), concatAll))
}
}
func Flatten[A any](mma [][]A) []A {
return G.Flatten(mma)
}
func Slice[A any](low, high int) func(as []A) []A {
return array.Slice[[]A](low, high)
}
func Lookup[A any](idx int) func([]A) O.Option[A] {
return G.Lookup[[]A](idx)
}
func UpsertAt[A any](a A) EM.Endomorphism[[]A] {
return G.UpsertAt[[]A](a)
}
func Size[A any](as []A) int {
return G.Size(as)
}
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]()
}
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)
}
func Push[A any](a A) EM.Endomorphism[[]A] {
return G.Push[EM.Endomorphism[[]A]](a)
}
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)
}
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)
}
func Prepend[A any](head A) EM.Endomorphism[[]A] {
return G.Prepend[EM.Endomorphism[[]A]](head)
}

205
v2/array/array_test.go Normal file
View File

@@ -0,0 +1,205 @@
// Copyright (c) 2023 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 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
}

66
v2/array/bind.go Normal file
View File

@@ -0,0 +1,66 @@
// Copyright (c) 2023 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"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
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]
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 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([]S1) []S2 {
return G.Let[[]S1, []S2, 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([]S1) []S2 {
return G.LetTo[[]S1, []S2, 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([]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
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)
}

56
v2/array/bind_test.go Normal file
View File

@@ -0,0 +1,56 @@
// Copyright (c) 2023 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"))
}

40
v2/array/eq.go Normal file
View File

@@ -0,0 +1,40 @@
// Copyright (c) 2023 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
}
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)
})
}

View File

@@ -0,0 +1,77 @@
// Copyright (c) 2023 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
}

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2023 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]
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2023 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}}]
}

61
v2/array/find.go Normal file
View File

@@ -0,0 +1,61 @@
// Copyright (c) 2023 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 (or a refinement) function
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 (or a refinement) function
func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
return G.FindFirstWithIndex[[]A](pred)
}
// FindFirstMap finds the first element returned by an [O.Option] based selector function
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 returned by an [O.Option] based selector function
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 (or a refinement) function
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 (or a refinement) function
func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
return G.FindLastWithIndex[[]A](pred)
}
// FindLastMap finds the Last element returned by an [O.Option] based selector function
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 returned by an [O.Option] based selector function
func FindLastMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] {
return G.FindLastMapWithIndex[[]A](sel)
}

34
v2/array/generic/any.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 2023 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))
}

352
v2/array/generic/array.go Normal file
View File

@@ -0,0 +1,352 @@
// Copyright (c) 2023 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 {
return array.Reduce(fa, func(bs GB, a A) GB {
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
}, Empty[GB]())
}
func filterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB {
return array.ReduceWithIndex(fa, func(idx int, bs GB, a A) GB {
return O.MonadFold(f(idx, a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
}, Empty[GB]())
}
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 {
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 m.Concat(cur, f(a))
}, m.Empty())
}
}
}
func FoldMapWithIndex[AS ~[]A, A, B any](m M.Monoid[B]) func(func(int, A) B) func(AS) B {
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 m.Concat(cur, f(idx, a))
}, m.Empty())
}
}
}
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
return func(as AS) A {
return array.Reduce(as, m.Concat, m.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
View File

@@ -0,0 +1,89 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,97 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2024 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
View File

@@ -0,0 +1,56 @@
// Copyright (c) 2023 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
View 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
View File

@@ -0,0 +1,52 @@
// Copyright (c) 2023 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)
}

24
v2/array/magma.go Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2023 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"
)
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
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2023 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)
}

26
v2/array/monad.go Normal file
View File

@@ -0,0 +1,26 @@
// Copyright (c) 2024 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
func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] {
return G.Monad[A, B, []A, []B, []func(A) B]()
}

63
v2/array/monoid.go Normal file
View File

@@ -0,0 +1,63 @@
// Copyright (c) 2023 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
}
func Monoid[T any]() M.Monoid[[]T] {
return M.MakeMonoid(concat[T], Empty[T]())
}
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)
}
// ConcatAll efficiently concatenates the input arrays into a final array
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
View File

@@ -0,0 +1,26 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,136 @@
// Copyright (c) 2023 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)
}

58
v2/array/sequence.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright (c) 2023 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"
)
// We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
// HKTA = HKT<A>
// HKTRA = HKT<[]A>
// HKTFRA = HKT<func(A)[]A>
// Sequence takes an `Array` where elements are `HKT<A>` (higher kinded type) and,
// using an applicative of that `HKT`, returns an `HKT` of `[]A`.
// e.g. it can turn an `[]Either[error, string]` into an `Either[error, []string]`.
//
// Sequence requires an `Applicative` of the `HKT` you are targeting, e.g. to turn an
// `[]Either[E, A]` into an `Either[E, []A]`, it needs an
// Applicative` for `Either`, to to turn an `[]Option[A]` into an `Option[ []A]`,
// it needs an `Applicative` for `Option`.
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])
return Reduce(func(fas HKTRA, fa HKTA) HKTRA {
return _ap(
_map(fas, ca),
fa,
)
}, _of(Empty[A]()))
}
// ArrayOption returns a function to convert sequence of options into an option of a sequence
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
View File

@@ -0,0 +1,31 @@
// Copyright (c) 2023 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]()}))
}

36
v2/array/sort.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2023 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
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
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 given the provided ordering
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
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,74 @@
// Copyright (c) 2023 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,
)
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2023 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))
}

40
v2/array/traverse.go Normal file
View File

@@ -0,0 +1,40 @@
// Copyright (c) 2023 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"
)
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)
}
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
View File

@@ -0,0 +1,43 @@
// Copyright (c) 2023 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}))
}

17
v2/array/uniq.go Normal file
View File

@@ -0,0 +1,17 @@
package array
import (
G "github.com/IBM/fp-go/v2/array/generic"
)
// 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[A comparable](as []A) []A {
return G.StrictUniq[[]A](as)
}
// 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[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
View 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)
}

38
v2/array/zip.go Normal file
View File

@@ -0,0 +1,38 @@
// Copyright (c) 2023 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 short, excess elements of the longer array are discarded.
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. If one input array is short, excess elements of the
// longer array are discarded
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 function is reverse of [Zip]. Takes an array of pairs and return two corresponding arrays
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
View File

@@ -0,0 +1,56 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,109 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,59 @@
// Copyright (c) 2023 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
})
)

22
v2/boolean/types.go Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,62 @@
// Copyright (c) 2023 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())
}

28
v2/bytes/bytes.go Normal file
View File

@@ -0,0 +1,28 @@
// Copyright (c) 2023 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)
}

34
v2/bytes/monoid.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,26 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,432 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,294 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,39 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,39 @@
// Copyright (c) 2023 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",
}
)

View File

@@ -0,0 +1,271 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,231 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,200 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,31 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,103 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,112 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,283 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,117 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,376 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,413 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,210 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,433 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,164 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,294 @@
// Copyright (c) 2023 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),
)
},
}
}

View 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
View File

@@ -0,0 +1,625 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,59 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,40 @@
// Copyright (c) 2023 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")))
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2023 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
View File

@@ -0,0 +1,17 @@
// Copyright (c) 2023 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

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2023 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()
})
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2023 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()
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2023 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"))
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2023 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)
}
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2023 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))
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2023 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)
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2023 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]]
)

View File

@@ -0,0 +1,89 @@
// Copyright (c) 2023 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,
) func(ReaderIOEither[T]) ReaderIOEither[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,
)
}

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2023 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"))
}

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2023 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,
)
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2023 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
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