mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-28 19:49:07 +02:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a516849c07 | ||
|
29200d34dc | ||
|
7a3989989b | ||
|
6a6d53f025 | ||
|
078da752cd | ||
|
1a489fde27 | ||
|
a135b2acae | ||
|
9e9dfa1f5f | ||
|
dd87ea12b3 | ||
|
5fc0d18c97 | ||
|
76c1297576 | ||
|
68aeb4c725 | ||
|
53f3fa1828 | ||
|
97e1e4d92d | ||
|
ec57d5cd4a | ||
|
0ae5b43724 | ||
|
e73e14c0ae | ||
|
325bc376f9 | ||
|
f646ace9fe | ||
|
9f8161fbc1 | ||
|
1c4f2c0403 | ||
|
3b3b80aed0 | ||
|
fdff4e4735 | ||
|
391754e5a6 |
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -18,7 +18,7 @@ env:
|
||||
# Currently no way to detect automatically
|
||||
DEFAULT_BRANCH: main
|
||||
GO_VERSION: 1.21.6 # renovate: datasource=golang-version depName=golang
|
||||
NODE_VERSION: 20
|
||||
NODE_VERSION: 22
|
||||
DRY_RUN: true
|
||||
|
||||
jobs:
|
||||
@@ -26,10 +26,10 @@ jobs:
|
||||
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']
|
||||
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 }}
|
||||
@@ -55,12 +55,12 @@ jobs:
|
||||
steps:
|
||||
# full checkout for semantic-release
|
||||
- name: Full checkout
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
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
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
|
||||
- name: 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 }}
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
fp-go.exe
|
||||
fp-go
|
||||
main.exe
|
||||
build/
|
||||
.idea
|
@@ -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
|
||||
|
||||
|
@@ -1,2 +1,3 @@
|
||||
@echo off
|
||||
go tool cover -html=build/cover.out -o build/cover.html
|
||||
go tool cover -html=build/cover.out -o build/cover.html
|
||||
cov-report -ex ".*/cli/.*.go|.*/gen.go|.*/binds.go" build\cover.out
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
TST "github.com/IBM/fp-go/internal/testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -16,3 +18,33 @@ func TestCompactArray(t *testing.T) {
|
||||
res := CompactArray(ar)
|
||||
assert.Equal(t, 2, len(res))
|
||||
}
|
||||
|
||||
func TestSequenceArray(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayTest(
|
||||
FromStrictEquals[error, bool](),
|
||||
Pointed[error, string](),
|
||||
Pointed[error, bool](),
|
||||
Functor[error, []string, bool](),
|
||||
SequenceArray[error, string],
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceArrayError(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayErrorTest(
|
||||
FromStrictEquals[error, bool](),
|
||||
Left[string, error],
|
||||
Left[bool, error],
|
||||
Pointed[error, string](),
|
||||
Pointed[error, bool](),
|
||||
Functor[error, []string, bool](),
|
||||
SequenceArray[error, string],
|
||||
)
|
||||
// run across four bits
|
||||
s(4)(t)
|
||||
}
|
||||
|
31
either/functor.go
Normal file
31
either/functor.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 either
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/internal/functor"
|
||||
)
|
||||
|
||||
type eitherFunctor[E, A, B any] struct{}
|
||||
|
||||
func (o *eitherFunctor[E, A, B]) Map(f func(A) B) func(Either[E, A]) Either[E, B] {
|
||||
return Map[E, A, B](f)
|
||||
}
|
||||
|
||||
// Functor implements the functoric operations for [Either]
|
||||
func Functor[E, A, B any]() functor.Functor[A, B, Either[E, A], Either[E, B]] {
|
||||
return &eitherFunctor[E, A, B]{}
|
||||
}
|
31
either/pointed.go
Normal file
31
either/pointed.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 either
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
)
|
||||
|
||||
type eitherPointed[E, A any] struct{}
|
||||
|
||||
func (o *eitherPointed[E, A]) Of(a A) Either[E, A] {
|
||||
return Of[E, A](a)
|
||||
}
|
||||
|
||||
// Pointed implements the pointedic operations for [Either]
|
||||
func Pointed[E, A any]() pointed.Pointed[A, Either[E, A]] {
|
||||
return &eitherPointed[E, A]{}
|
||||
}
|
8
go.mod
8
go.mod
@@ -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.6
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
18
go.sum
18
go.sum
@@ -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,16 @@ 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/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
|
||||
github.com/urfave/cli/v2 v2.27.6/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=
|
||||
|
@@ -62,3 +62,122 @@ func SequenceArrayTest[
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SequenceArrayErrorTest tests if the sequence operation works in case the operation can error
|
||||
func SequenceArrayErrorTest[
|
||||
HKTA,
|
||||
HKTB,
|
||||
HKTAA any, // HKT[[]A]
|
||||
](
|
||||
eq EQ.Eq[HKTB],
|
||||
|
||||
left func(error) HKTA,
|
||||
leftB func(error) HKTB,
|
||||
pa pointed.Pointed[string, HKTA],
|
||||
pb pointed.Pointed[bool, HKTB],
|
||||
faa functor.Functor[[]string, bool, HKTAA, HKTB],
|
||||
seq func([]HKTA) HKTAA,
|
||||
) func(count int) func(t *testing.T) {
|
||||
|
||||
return func(count int) func(t *testing.T) {
|
||||
|
||||
expGood := make([]string, count)
|
||||
good := make([]HKTA, count)
|
||||
expBad := make([]error, count)
|
||||
bad := make([]HKTA, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
goodVal := fmt.Sprintf("TestData %d", i)
|
||||
badVal := fmt.Errorf("ErrorData %d", i)
|
||||
expGood[i] = goodVal
|
||||
good[i] = pa.Of(goodVal)
|
||||
expBad[i] = badVal
|
||||
bad[i] = left(badVal)
|
||||
}
|
||||
|
||||
total := 1 << count
|
||||
|
||||
return func(t *testing.T) {
|
||||
// test the good case
|
||||
res := F.Pipe2(
|
||||
good,
|
||||
seq,
|
||||
faa.Map(func(act []string) bool {
|
||||
return assert.Equal(t, expGood, act)
|
||||
}),
|
||||
)
|
||||
assert.True(t, eq.Equals(res, pb.Of(true)))
|
||||
// iterate and test the bad cases
|
||||
for i := 1; i < total; i++ {
|
||||
// run the test
|
||||
t.Run(fmt.Sprintf("Bitmask test %d", i), func(t1 *testing.T) {
|
||||
// the actual
|
||||
act := make([]HKTA, count)
|
||||
// the expected error
|
||||
var exp error
|
||||
// prepare the values bases on the bit mask
|
||||
mask := 1
|
||||
for j := 0; j < count; j++ {
|
||||
if (i & mask) == 0 {
|
||||
act[j] = good[j]
|
||||
} else {
|
||||
act[j] = bad[j]
|
||||
if exp == nil {
|
||||
exp = expBad[j]
|
||||
}
|
||||
}
|
||||
mask <<= 1
|
||||
}
|
||||
// test the good case
|
||||
res := F.Pipe2(
|
||||
act,
|
||||
seq,
|
||||
faa.Map(func(act []string) bool {
|
||||
return assert.Equal(t, expGood, act)
|
||||
}),
|
||||
)
|
||||
// validate the error
|
||||
assert.True(t, eq.Equals(res, leftB(exp)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ package generic
|
||||
|
||||
import (
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
"github.com/IBM/fp-go/internal/functor"
|
||||
"github.com/IBM/fp-go/internal/monad"
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
)
|
||||
@@ -25,6 +26,8 @@ type ioEitherPointed[E, A any, GA ~func() ET.Either[E, A]] struct{}
|
||||
|
||||
type ioEitherMonad[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B]] struct{}
|
||||
|
||||
type ioEitherFunctor[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B]] struct{}
|
||||
|
||||
func (o *ioEitherPointed[E, A, GA]) Of(a A) GA {
|
||||
return Of[GA, E, A](a)
|
||||
}
|
||||
@@ -45,11 +48,20 @@ func (o *ioEitherMonad[E, A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
|
||||
return Ap[GB, GAB, GA, E, A, B](fa)
|
||||
}
|
||||
|
||||
func (o *ioEitherFunctor[E, A, B, GA, GB]) Map(f func(A) B) func(GA) GB {
|
||||
return Map[GA, GB, E, A, B](f)
|
||||
}
|
||||
|
||||
// Pointed implements the pointed operations for [IOEither]
|
||||
func Pointed[E, A any, GA ~func() ET.Either[E, A]]() pointed.Pointed[A, GA] {
|
||||
return &ioEitherPointed[E, A, GA]{}
|
||||
}
|
||||
|
||||
// Functor implements the monadic operations for [IOEither]
|
||||
func Functor[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B]]() functor.Functor[A, B, GA, GB] {
|
||||
return &ioEitherFunctor[E, A, B, GA, GB]{}
|
||||
}
|
||||
|
||||
// Monad implements the monadic operations for [IOEither]
|
||||
func Monad[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B]]() monad.Monad[A, B, GA, GB, GAB] {
|
||||
return &ioEitherMonad[E, A, B, GA, GB, GAB]{}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/internal/functor"
|
||||
"github.com/IBM/fp-go/internal/monad"
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
G "github.com/IBM/fp-go/ioeither/generic"
|
||||
@@ -26,6 +27,11 @@ func Pointed[E, A any]() pointed.Pointed[A, IOEither[E, A]] {
|
||||
return G.Pointed[E, A, IOEither[E, A]]()
|
||||
}
|
||||
|
||||
// Functor returns the functor operations for [IOEither]
|
||||
func Functor[E, A, B any]() functor.Functor[A, B, IOEither[E, A], IOEither[E, B]] {
|
||||
return G.Functor[E, A, B, IOEither[E, A], IOEither[E, B]]()
|
||||
}
|
||||
|
||||
// Monad returns the monadic operations for [IOEither]
|
||||
func Monad[E, A, B any]() monad.Monad[A, B, IOEither[E, A], IOEither[E, B], IOEither[E, func(A) B]] {
|
||||
return G.Monad[E, A, B, IOEither[E, A], IOEither[E, B], IOEither[E, func(A) B]]()
|
||||
|
@@ -16,11 +16,15 @@
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
TST "github.com/IBM/fp-go/internal/testing"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -46,3 +50,33 @@ func TestMapSeq(t *testing.T) {
|
||||
|
||||
assert.Equal(t, E.Of[error](true), res())
|
||||
}
|
||||
|
||||
func TestSequenceArray(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayTest(
|
||||
FromStrictEquals[error, bool](),
|
||||
Pointed[error, string](),
|
||||
Pointed[error, bool](),
|
||||
Functor[error, []string, bool](),
|
||||
SequenceArray[error, string],
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceArrayError(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayErrorTest(
|
||||
FromStrictEquals[error, bool](),
|
||||
Left[string, error],
|
||||
Left[bool, error],
|
||||
Pointed[error, string](),
|
||||
Pointed[error, bool](),
|
||||
Functor[error, []string, bool](),
|
||||
SequenceArray[error, string],
|
||||
)
|
||||
// run across four bits
|
||||
s(4)(t)
|
||||
}
|
||||
|
@@ -19,8 +19,7 @@ import (
|
||||
G "github.com/IBM/fp-go/iterator/stateless/generic"
|
||||
)
|
||||
|
||||
// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element.
|
||||
// Note, the [Iterator] does not produce any output until the predicate first becomes false
|
||||
// Cycle creates an [Iterator] containing an [Iterator] repeated an infinite number of times.
|
||||
func Cycle[U any](ma Iterator[U]) Iterator[U] {
|
||||
return G.Cycle[Iterator[U]](ma)
|
||||
}
|
||||
|
@@ -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))
|
||||
}
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -17,11 +17,13 @@ package readerioeither
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
TST "github.com/IBM/fp-go/internal/testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -37,3 +39,33 @@ func TestTraverseArray(t *testing.T) {
|
||||
assert.Equal(t, ET.Right[string]([]string{"aa", "bb"}), F.Pipe1([]string{"a", "b"}, f)(ctx)())
|
||||
assert.Equal(t, ET.Left[[]string]("e"), F.Pipe1([]string{"a", ""}, f)(ctx)())
|
||||
}
|
||||
|
||||
func TestSequenceArray(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayTest(
|
||||
FromStrictEquals[context.Context, error, bool]()(context.Background()),
|
||||
Pointed[context.Context, error, string](),
|
||||
Pointed[context.Context, error, bool](),
|
||||
Functor[context.Context, error, []string, bool](),
|
||||
SequenceArray[context.Context, error, string],
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceArrayError(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayErrorTest(
|
||||
FromStrictEquals[context.Context, error, bool]()(context.Background()),
|
||||
Left[context.Context, string, error],
|
||||
Left[context.Context, bool, error],
|
||||
Pointed[context.Context, error, string](),
|
||||
Pointed[context.Context, error, bool](),
|
||||
Functor[context.Context, error, []string, bool](),
|
||||
SequenceArray[context.Context, error, string],
|
||||
)
|
||||
// run across four bits
|
||||
s(4)(t)
|
||||
}
|
||||
|
68
readerioeither/generic/monad.go
Normal file
68
readerioeither/generic/monad.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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 (
|
||||
ET "github.com/IBM/fp-go/either"
|
||||
"github.com/IBM/fp-go/internal/functor"
|
||||
"github.com/IBM/fp-go/internal/monad"
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
)
|
||||
|
||||
type readerIOEitherPointed[R, E, A any, GRA ~func(R) GIOA, GIOA ~func() ET.Either[E, A]] struct{}
|
||||
|
||||
type readerIOEitherMonad[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GRAB ~func(R) GIOAB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GIOAB ~func() ET.Either[E, func(A) B]] struct{}
|
||||
|
||||
type readerIOEitherFunctor[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B]] struct{}
|
||||
|
||||
func (o *readerIOEitherPointed[R, E, A, GRA, GIOA]) Of(a A) GRA {
|
||||
return Of[GRA, GIOA, R, E, A](a)
|
||||
}
|
||||
|
||||
func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Of(a A) GRA {
|
||||
return Of[GRA, GIOA, R, E, A](a)
|
||||
}
|
||||
|
||||
func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Map(f func(A) B) func(GRA) GRB {
|
||||
return Map[GRA, GRB, GIOA, GIOB, R, E, A, B](f)
|
||||
}
|
||||
|
||||
func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Chain(f func(A) GRB) func(GRA) GRB {
|
||||
return Chain[GRA, GRB, GIOA, GIOB, R, E, A, B](f)
|
||||
}
|
||||
|
||||
func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Ap(fa GRA) func(GRAB) GRB {
|
||||
return Ap[GRA, GRB, GRAB, GIOA, GIOB, GIOAB, R, E, A, B](fa)
|
||||
}
|
||||
|
||||
func (o *readerIOEitherFunctor[R, E, A, B, GRA, GRB, GIOA, GIOB]) Map(f func(A) B) func(GRA) GRB {
|
||||
return Map[GRA, GRB, GIOA, GIOB, R, E, A, B](f)
|
||||
}
|
||||
|
||||
// Pointed implements the pointed operations for [ReaderIOEither]
|
||||
func Pointed[R, E, A any, GRA ~func(R) GIOA, GIOA ~func() ET.Either[E, A]]() pointed.Pointed[A, GRA] {
|
||||
return &readerIOEitherPointed[R, E, A, GRA, GIOA]{}
|
||||
}
|
||||
|
||||
// Functor implements the monadic operations for [ReaderIOEither]
|
||||
func Functor[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B]]() functor.Functor[A, B, GRA, GRB] {
|
||||
return &readerIOEitherFunctor[R, E, A, B, GRA, GRB, GIOA, GIOB]{}
|
||||
}
|
||||
|
||||
// Monad implements the monadic operations for [ReaderIOEither]
|
||||
func Monad[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GRAB ~func(R) GIOAB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GIOAB ~func() ET.Either[E, func(A) B]]() monad.Monad[A, B, GRA, GRB, GRAB] {
|
||||
return &readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]{}
|
||||
}
|
38
readerioeither/monad.go
Normal file
38
readerioeither/monad.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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 readerioeither
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/internal/functor"
|
||||
"github.com/IBM/fp-go/internal/monad"
|
||||
"github.com/IBM/fp-go/internal/pointed"
|
||||
G "github.com/IBM/fp-go/readerioeither/generic"
|
||||
)
|
||||
|
||||
// Pointed returns the pointed operations for [ReaderIOEither]
|
||||
func Pointed[R, E, A any]() pointed.Pointed[A, ReaderIOEither[R, E, A]] {
|
||||
return G.Pointed[R, E, A, ReaderIOEither[R, E, A]]()
|
||||
}
|
||||
|
||||
// Functor returns the functor operations for [ReaderIOEither]
|
||||
func Functor[R, E, A, B any]() functor.Functor[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]] {
|
||||
return G.Functor[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]()
|
||||
}
|
||||
|
||||
// Monad returns the monadic operations for [ReaderIOEither]
|
||||
func Monad[R, E, A, B any]() monad.Monad[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]] {
|
||||
return G.Monad[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]]()
|
||||
}
|
@@ -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
48
record/eq_test.go
Normal 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))
|
||||
}
|
@@ -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]())
|
||||
}
|
||||
|
@@ -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))
|
||||
}
|
||||
|
@@ -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))
|
||||
}
|
||||
|
Reference in New Issue
Block a user