1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-11 23:17:16 +02:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Dr. Carsten Leue
c6d6be66e0 fix: add missing ChainXXIOK to Reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-06 22:36:04 +02:00
Carsten Leue
e313f95865 Merge pull request #60 from IBM/cleue-alternative-monoid-for-option
fix: provide AltMonoid
2023-10-06 21:50:57 +02:00
Dr. Carsten Leue
5f25317f97 fix: provide AltMonoid
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-10-06 21:50:22 +02:00
Carsten Leue
f5aa2d6c15 Merge pull request #58 from IBM/renovate/major-go-dependencies
chore(deps): update actions/checkout action to v4
2023-10-05 15:20:29 +02:00
renovate[bot]
7262820624 chore(deps): update actions/checkout action to v4 2023-10-05 12:37:09 +00:00
Carsten Leue
c8fc10358b Merge pull request #56 from IBM/cleue-add-flap
fix: add flap and non empty array
2023-09-26 22:33:30 +02:00
Dr. Carsten Leue
1cd167541d fix: add flap and non empty array
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-26 22:32:53 +02:00
Carsten Leue
ebe94d71dc Merge pull request #55 from IBM/sample-match
fix: add match example
2023-09-24 22:17:05 +02:00
Dr. Carsten Leue
7ef6eb524b fix: add match example
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-24 22:15:01 +02:00
Carsten Leue
7b82e87bf3 Merge pull request #53 from IBM/cleue-add-missing-flatten-to-reader
fix: add missing Flatten to Reader
2023-09-20 17:54:07 +02:00
Dr. Carsten Leue
e8fdbe9f87 fix: add missing Flatten to Reader
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-20 17:53:45 +02:00
Carsten Leue
226c7039de Merge pull request #52 from IBM/cleue-add-memoize-to-reader
fix: add missing Memoize to readers
2023-09-20 16:04:51 +02:00
Dr. Carsten Leue
943ae8e009 fix: add missing Memoize to readers
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-20 15:56:02 +02:00
Carsten Leue
44c8441b07 Merge pull request #51 from IBM/cleue-rioe-tests
fix: add RIOE testcases
2023-09-19 22:33:56 +02:00
Dr. Carsten Leue
600aeae770 fix: add RIOE testcases
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-19 22:31:55 +02:00
Carsten Leue
f74a407294 Merge pull request #50 from IBM/cleue-add-some-tweaks
fix: add WithTempFile to ReaderIOEither
2023-09-19 18:07:06 +02:00
36 changed files with 808 additions and 45 deletions

View File

@@ -29,7 +29,7 @@ jobs:
go-version: [ '1.20.x', '1.21.x' ] go-version: [ '1.20.x', '1.21.x' ]
steps: steps:
# full checkout for semantic-release # full checkout for semantic-release
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up go ${{ matrix.go-version }} - name: Set up go ${{ matrix.go-version }}
@@ -55,7 +55,7 @@ jobs:
steps: steps:
# full checkout for semantic-release # full checkout for semantic-release
- name: Full checkout - name: Full checkout
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
with: with:
fetch-depth: 0 fetch-depth: 0

View File

@@ -308,3 +308,11 @@ func Fold[A any](m M.Monoid[A]) func([]A) A {
func Push[A any](a A) func([]A) []A { func Push[A any](a A) func([]A) []A {
return G.Push[[]A](a) return G.Push[[]A](a)
} }
func MonadFlap[A, B any](fab []func(A) B, a A) []B {
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
}
func Flap[A, B any](a A) func([]func(A) B) []B {
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
}

View File

@@ -18,6 +18,7 @@ package generic
import ( import (
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/array" "github.com/IBM/fp-go/internal/array"
FC "github.com/IBM/fp-go/internal/functor"
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option" O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/tuple" "github.com/IBM/fp-go/tuple"
@@ -250,3 +251,11 @@ func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
func Push[GA ~[]A, A any](a A) func(GA) GA { func Push[GA ~[]A, A any](a A) func(GA) GA {
return F.Bind2nd(array.Push[GA, A], a) 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 F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
}

124
array/nonempty/array.go Normal file
View File

@@ -0,0 +1,124 @@
// 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/array/generic"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/array"
S "github.com/IBM/fp-go/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](as NonEmptyArray[A]) bool {
return false
}
func IsNonEmpty[A any](as 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 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))
}
}

View File

@@ -18,6 +18,8 @@ package readerio
import ( import (
"context" "context"
IO "github.com/IBM/fp-go/io"
L "github.com/IBM/fp-go/lazy"
R "github.com/IBM/fp-go/readerio/generic" R "github.com/IBM/fp-go/readerio/generic"
) )
@@ -37,6 +39,22 @@ func Chain[A, B any](f func(A) ReaderIO[B]) func(ReaderIO[A]) ReaderIO[B] {
return R.Chain[ReaderIO[A]](f) return R.Chain[ReaderIO[A]](f)
} }
func MonadChainIOK[A, B any](fa ReaderIO[A], f func(A) IO.IO[B]) ReaderIO[B] {
return R.MonadChainIOK[ReaderIO[A], ReaderIO[B]](fa, f)
}
func ChainIOK[A, B any](f func(A) IO.IO[B]) func(ReaderIO[A]) ReaderIO[B] {
return R.ChainIOK[ReaderIO[A], ReaderIO[B]](f)
}
func MonadChainFirstIOK[A, B any](fa ReaderIO[A], f func(A) IO.IO[B]) ReaderIO[A] {
return R.MonadChainFirstIOK[ReaderIO[A], ReaderIO[B]](fa, f)
}
func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(ReaderIO[A]) ReaderIO[A] {
return R.ChainFirstIOK[ReaderIO[A], ReaderIO[B]](f)
}
func Of[A any](a A) ReaderIO[A] { func Of[A any](a A) ReaderIO[A] {
return R.Of[ReaderIO[A]](a) return R.Of[ReaderIO[A]](a)
} }
@@ -54,6 +72,18 @@ func Ask() ReaderIO[context.Context] {
} }
// Defer creates an IO by creating a brand new IO via a generator function, each time // Defer creates an IO by creating a brand new IO via a generator function, each time
func Defer[A any](gen func() ReaderIO[A]) ReaderIO[A] { func Defer[A any](gen L.Lazy[ReaderIO[A]]) ReaderIO[A] {
return R.Defer[ReaderIO[A]](gen) return R.Defer[ReaderIO[A]](gen)
} }
// Memoize computes the value of the provided [ReaderIO] monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[A any](rdr ReaderIO[A]) ReaderIO[A] {
return R.Memoize[ReaderIO[A]](rdr)
}
// Flatten converts a nested [ReaderIO] into a [ReaderIO]
func Flatten[A any](mma ReaderIO[ReaderIO[A]]) ReaderIO[A] {
return R.Flatten[ReaderIO[A]](mma)
}

View File

@@ -29,10 +29,9 @@ import (
) )
var ( var (
openIOE = IOE.Eitherize1(os.Open)
// Open opens a file for reading within the given context // Open opens a file for reading within the given context
Open = F.Flow3( Open = F.Flow3(
openIOE, IOEF.Open,
RIOE.FromIOEither[*os.File], RIOE.FromIOEither[*os.File],
RIOE.WithContext[*os.File], RIOE.WithContext[*os.File],
) )
@@ -46,11 +45,11 @@ var (
// Close closes an object // Close closes an object
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] { func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
return RIOE.FromIOEither(func() ET.Either[error, any] { return F.Pipe2(
return ET.TryCatchError(func() (any, error) { c,
return c, c.Close() IOEF.Close[C],
}) RIOE.FromIOEither[any],
}) )
} }
// ReadFile reads a file in the scope of a context // ReadFile reads a file in the scope of a context

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 file
import (
"context"
"os"
"testing"
RIOE "github.com/IBM/fp-go/context/readerioeither"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/stretchr/testify/assert"
)
func TestWithTempFile(t *testing.T) {
res := WithTempFile(onWriteAll[*os.File]([]byte("Carsten")))
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
}
func TestWithTempFileOnClosedFile(t *testing.T) {
res := WithTempFile(func(f *os.File) RIOE.ReaderIOEither[[]byte] {
return F.Pipe2(
f,
onWriteAll[*os.File]([]byte("Carsten")),
RIOE.ChainFirst(F.Constant1[[]byte](Close(f))),
)
})
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
}

View File

@@ -0,0 +1,57 @@
// 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 file
import (
"context"
"io"
RIOE "github.com/IBM/fp-go/context/readerioeither"
F "github.com/IBM/fp-go/function"
)
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
return func(w W) RIOE.ReaderIOEither[[]byte] {
return F.Pipe1(
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) {
return func() ([]byte, error) {
_, err := w.Write(data)
return data, err
}
}),
RIOE.WithContext[[]byte],
)
}
}
// WriteAll uses a generator function to create a stream, writes data to it and closes it
func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
onWrite := onWriteAll[W](data)
return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
return RIOE.WithResource[[]byte](
onCreate,
Close[W])(
onWrite,
)
}
}
// Write uses a generator function to create a stream, writes data to it and closes it
func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOEither[W]) func(use func(W) RIOE.ReaderIOEither[R]) RIOE.ReaderIOEither[R] {
return RIOE.WithResource[R](
acquire,
Close[W])
}

View File

@@ -573,3 +573,22 @@ func MonadAlt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.
func Alt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](second LAZY) func(GEA) GEA { func Alt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](second LAZY) func(GEA) GEA {
return RIE.Alt(second) return RIE.Alt(second)
} }
// Memoize computes the value of the provided monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[
GRA ~func(context.Context) GIOA,
GIOA ~func() E.Either[error, A],
A any](rdr GRA) GRA {
return RIE.Memoize[GRA](rdr)
}
func Flatten[
GGRA ~func(context.Context) GGIOA,
GGIOA ~func() E.Either[error, GRA],
GRA ~func(context.Context) GIOA,
GIOA ~func() E.Either[error, A],
A any](rdr GGRA) GRA {
return RIE.Flatten[GRA](rdr)
}

View File

@@ -17,6 +17,7 @@ package readerioeither
import ( import (
G "github.com/IBM/fp-go/context/readerioeither/generic" G "github.com/IBM/fp-go/context/readerioeither/generic"
L "github.com/IBM/fp-go/lazy"
M "github.com/IBM/fp-go/monoid" M "github.com/IBM/fp-go/monoid"
) )
@@ -34,3 +35,22 @@ func ApplicativeMonoidSeq[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
func ApplicativeMonoidPar[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] { func ApplicativeMonoidPar[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
return G.ApplicativeMonoidPar[ReaderIOEither[A], ReaderIOEither[func(A) A]](m) return G.ApplicativeMonoidPar[ReaderIOEither[A], ReaderIOEither[func(A) A]](m)
} }
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOEither]
func AlternativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] {
return M.AlternativeMonoid(
Of[A],
MonadMap[A, func(A) A],
MonadAp[A, A],
MonadAlt[A],
m,
)
}
// AltMonoid is the alternative [Monoid] for an [ReaderIOEither]
func AltMonoid[A any](zero L.Lazy[ReaderIOEither[A]]) M.Monoid[ReaderIOEither[A]] {
return M.AltMonoid(
zero,
MonadAlt[A],
)
}

View File

@@ -155,6 +155,10 @@ func FromIO[A any](t IO.IO[A]) ReaderIOEither[A] {
return G.FromIO[ReaderIOEither[A]](t) return G.FromIO[ReaderIOEither[A]](t)
} }
func FromLazy[A any](t L.Lazy[A]) ReaderIOEither[A] {
return G.FromIO[ReaderIOEither[A]](t)
}
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled // Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
func Never[A any]() ReaderIOEither[A] { func Never[A any]() ReaderIOEither[A] {
return G.Never[ReaderIOEither[A]]() return G.Never[ReaderIOEither[A]]()
@@ -209,3 +213,16 @@ func MonadAlt[A any](first ReaderIOEither[A], second L.Lazy[ReaderIOEither[A]])
func Alt[A any](second L.Lazy[ReaderIOEither[A]]) func(ReaderIOEither[A]) ReaderIOEither[A] { func Alt[A any](second L.Lazy[ReaderIOEither[A]]) func(ReaderIOEither[A]) ReaderIOEither[A] {
return G.Alt(second) return G.Alt(second)
} }
// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
return G.Memoize[ReaderIOEither[A]](rdr)
}
// Flatten converts a nested [ReaderIOEither] into a [ReaderIOEither]
func Flatten[
A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
return G.Flatten[ReaderIOEither[ReaderIOEither[A]]](rdr)
}

View File

@@ -162,3 +162,125 @@ func TestRegularApply(t *testing.T) {
res := applied(context.Background())() res := applied(context.Background())()
assert.Equal(t, E.Of[error]("CARSTEN"), res) assert.Equal(t, E.Of[error]("CARSTEN"), res)
} }
func TestWithResourceNoErrors(t *testing.T) {
var countAcquire, countBody, countRelease int
acquire := FromLazy(func() int {
countAcquire++
return countAcquire
})
release := func(int) ReaderIOEither[int] {
return FromLazy(func() int {
countRelease++
return countRelease
})
}
body := func(int) ReaderIOEither[int] {
return FromLazy(func() int {
countBody++
return countBody
})
}
resRIOE := WithResource[int](acquire, release)(body)
res := resRIOE(context.Background())()
assert.Equal(t, 1, countAcquire)
assert.Equal(t, 1, countBody)
assert.Equal(t, 1, countRelease)
assert.Equal(t, E.Of[error](1), res)
}
func TestWithResourceErrorInBody(t *testing.T) {
var countAcquire, countBody, countRelease int
acquire := FromLazy(func() int {
countAcquire++
return countAcquire
})
release := func(int) ReaderIOEither[int] {
return FromLazy(func() int {
countRelease++
return countRelease
})
}
err := fmt.Errorf("error in body")
body := func(int) ReaderIOEither[int] {
return Left[int](err)
}
resRIOE := WithResource[int](acquire, release)(body)
res := resRIOE(context.Background())()
assert.Equal(t, 1, countAcquire)
assert.Equal(t, 0, countBody)
assert.Equal(t, 1, countRelease)
assert.Equal(t, E.Left[int](err), res)
}
func TestWithResourceErrorInAcquire(t *testing.T) {
var countAcquire, countBody, countRelease int
err := fmt.Errorf("error in acquire")
acquire := Left[int](err)
release := func(int) ReaderIOEither[int] {
return FromLazy(func() int {
countRelease++
return countRelease
})
}
body := func(int) ReaderIOEither[int] {
return FromLazy(func() int {
countBody++
return countBody
})
}
resRIOE := WithResource[int](acquire, release)(body)
res := resRIOE(context.Background())()
assert.Equal(t, 0, countAcquire)
assert.Equal(t, 0, countBody)
assert.Equal(t, 0, countRelease)
assert.Equal(t, E.Left[int](err), res)
}
func TestWithResourceErrorInRelease(t *testing.T) {
var countAcquire, countBody, countRelease int
acquire := FromLazy(func() int {
countAcquire++
return countAcquire
})
err := fmt.Errorf("error in release")
release := func(int) ReaderIOEither[int] {
return Left[int](err)
}
body := func(int) ReaderIOEither[int] {
return FromLazy(func() int {
countBody++
return countBody
})
}
resRIOE := WithResource[int](acquire, release)(body)
res := resRIOE(context.Background())()
assert.Equal(t, 1, countAcquire)
assert.Equal(t, 1, countBody)
assert.Equal(t, 0, countRelease)
assert.Equal(t, E.Left[int](err), res)
}

View File

@@ -22,6 +22,8 @@ package either
import ( import (
E "github.com/IBM/fp-go/errors" E "github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/lazy"
O "github.com/IBM/fp-go/option" O "github.com/IBM/fp-go/option"
) )
@@ -92,11 +94,11 @@ func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
} }
func MonadChainOptionK[A, B, E any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] { func MonadChainOptionK[A, B, E any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] {
return MonadChain(ma, F.Flow2(f, FromOption[E, B](onNone))) return MonadChain(ma, F.Flow2(f, FromOption[B](onNone)))
} }
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] { func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
from := FromOption[E, B](onNone) from := FromOption[B](onNone)
return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] { return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
return Chain(F.Flow2(f, from)) return Chain(F.Flow2(f, from))
} }
@@ -142,7 +144,7 @@ func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Eithe
} }
} }
func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] { func FromOption[A, E any](onNone func() E) func(O.Option[A]) Either[E, A] {
return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A]) return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A])
} }
@@ -197,11 +199,11 @@ func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B {
) )
} }
func AltW[E, E1, A any](that func() Either[E1, A]) func(Either[E, A]) Either[E1, A] { func AltW[E, E1, A any](that L.Lazy[Either[E1, A]]) func(Either[E, A]) Either[E1, A] {
return Fold(F.Ignore1of1[E](that), Right[E1, A]) return Fold(F.Ignore1of1[E](that), Right[E1, A])
} }
func Alt[E, A any](that func() Either[E, A]) func(Either[E, A]) Either[E, A] { func Alt[E, A any](that L.Lazy[Either[E, A]]) func(Either[E, A]) Either[E, A] {
return AltW[E](that) return AltW[E](that)
} }
@@ -245,3 +247,15 @@ func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3
func Swap[E, A any](val Either[E, A]) Either[A, E] { func Swap[E, A any](val Either[E, A]) Either[A, E] {
return MonadFold(val, Right[A, E], Left[E, A]) return MonadFold(val, Right[A, E], Left[E, A])
} }
func MonadFlap[E, A, B any](fab Either[E, func(A) B], a A) Either[E, B] {
return FC.MonadFlap(MonadMap[E, func(A) B, B], fab, a)
}
func Flap[E, A, B any](a A) func(Either[E, func(A) B]) Either[E, B] {
return F.Bind2nd(MonadFlap[E, A, B], a)
}
func MonadAlt[E, A any](fa Either[E, A], that L.Lazy[Either[E, A]]) Either[E, A] {
return MonadFold(fa, F.Ignore1of1[E](that), Of[E, A])
}

View File

@@ -112,6 +112,6 @@ func TestChainOptionK(t *testing.T) {
} }
func TestFromOption(t *testing.T) { func TestFromOption(t *testing.T) {
assert.Equal(t, Left[int]("none"), FromOption[string, int](F.Constant("none"))(O.None[int]())) assert.Equal(t, Left[int]("none"), FromOption[int](F.Constant("none"))(O.None[int]()))
assert.Equal(t, Right[string](1), FromOption[string, int](F.Constant("none"))(O.Some(1))) assert.Equal(t, Right[string](1), FromOption[int](F.Constant("none"))(O.Some(1)))
} }

40
either/monoid.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 either
import (
L "github.com/IBM/fp-go/lazy"
M "github.com/IBM/fp-go/monoid"
)
// AlternativeMonoid is the alternative [Monoid] for an [Either]
func AlternativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
return M.AlternativeMonoid(
Of[E, A],
MonadMap[E, A, func(A) A],
MonadAp[A, E, A],
MonadAlt[E, A],
m,
)
}
// AltMonoid is the alternative [Monoid] for an [Either]
func AltMonoid[E, A any](zero L.Lazy[Either[E, A]]) M.Monoid[Either[E, A]] {
return M.AltMonoid(
zero,
MonadAlt[E, A],
)
}

View File

@@ -13,17 +13,15 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package file package either
import ( import (
"io" S "github.com/IBM/fp-go/semigroup"
IOE "github.com/IBM/fp-go/ioeither"
) )
// onClose closes a closeable resource // AltSemigroup is the alternative [Semigroup] for an [Either]
func onClose[R io.Closer](r R) IOE.IOEither[error, R] { func AltSemigroup[E, A any]() S.Semigroup[Either[E, A]] {
return IOE.TryCatchError(func() (R, error) { return S.AltSemigroup(
return r, r.Close() MonadAlt[E, A],
}) )
} }

View File

@@ -57,7 +57,7 @@ var (
GetHeader, GetHeader,
R.Lookup[H.Header](HeaderContentType), R.Lookup[H.Header](HeaderContentType),
O.Chain(A.First[string]), O.Chain(A.First[string]),
E.FromOption[error, string](errors.OnNone("unable to access the [%s] header", HeaderContentType)), E.FromOption[string](errors.OnNone("unable to access the [%s] header", HeaderContentType)),
E.ChainFirst(validateJsonContentTypeString), E.ChainFirst(validateJsonContentTypeString),
))) )))
) )

View File

@@ -18,6 +18,7 @@ package generic
import ( import (
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain" C "github.com/IBM/fp-go/internal/chain"
FC "github.com/IBM/fp-go/internal/functor"
) )
func MonadAp[GAB ~func(A) B, B, A any](fab GAB, fa A) B { func MonadAp[GAB ~func(A) B, B, A any](fab GAB, fa A) B {
@@ -51,3 +52,11 @@ func MonadChainFirst[GAB ~func(A) B, A, B any](fa A, f GAB) A {
func ChainFirst[GAB ~func(A) B, A, B any](f GAB) func(A) A { func ChainFirst[GAB ~func(A) B, A, B any](f GAB) func(A) A {
return C.ChainFirst(MonadChain[func(A) A, A, A], MonadMap[func(B) A, B, A], f) return C.ChainFirst(MonadChain[func(A) A, A, A], MonadMap[func(B) A, B, A], f)
} }
func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B {
return FC.MonadFlap(MonadMap[func(GAB) B, GAB, B], fab, a)
}
func Flap[GAB ~func(A) B, A, B any](a A) func(GAB) B {
return F.Bind2nd(MonadFlap[GAB, A, B], a)
}

View File

@@ -63,3 +63,11 @@ func MonadChainFirst[A, B any](fa A, f func(A) B) A {
func ChainFirst[A, B any](f func(A) B) func(A) A { func ChainFirst[A, B any](f func(A) B) func(A) A {
return G.ChainFirst(f) return G.ChainFirst(f)
} }
func MonadFlap[A, B any](fab func(A) B, a A) B {
return G.MonadFlap[func(A) B](fab, a)
}
func Flap[A, B any](a A) func(func(A) B) B {
return G.Flap[func(A) B](a)
}

View File

@@ -22,8 +22,8 @@ import (
O "github.com/IBM/fp-go/option" O "github.com/IBM/fp-go/option"
) )
func FromOption[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, onNone func() E) func(ma O.Option[A]) HKTEA { func FromOption[A, HKTEA, E any](fromEither func(ET.Either[E, A]) HKTEA, onNone func() E) func(ma O.Option[A]) HKTEA {
return F.Flow2(ET.FromOption[E, A](onNone), fromEither) return F.Flow2(ET.FromOption[A](onNone), fromEither)
} }
func FromPredicate[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, pred func(A) bool, onFalse func(A) E) func(A) HKTEA { func FromPredicate[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, pred func(A) bool, onFalse func(A) E) func(A) HKTEA {
@@ -45,7 +45,7 @@ func MonadFromOption[E, A, HKTEA any](
) )
} }
func FromOptionK[E, A, B, HKTEB any]( func FromOptionK[A, E, B, HKTEB any](
fromEither func(ET.Either[E, B]) HKTEB, fromEither func(ET.Either[E, B]) HKTEB,
onNone func() E) func(f func(A) O.Option[B]) func(A) HKTEB { onNone func() E) func(f func(A) O.Option[B]) func(A) HKTEB {
// helper // helper
@@ -65,7 +65,7 @@ func ChainOptionK[A, E, B, HKTEA, HKTEB any](
fromEither func(ET.Either[E, B]) HKTEB, fromEither func(ET.Either[E, B]) HKTEB,
onNone func() E, onNone func() E,
) func(f func(A) O.Option[B]) func(ma HKTEA) HKTEB { ) func(f func(A) O.Option[B]) func(ma HKTEA) HKTEB {
return F.Flow2(FromOptionK[E, A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain)) return F.Flow2(FromOptionK[A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain))
} }
func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any]( func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any](

37
internal/functor/flap.go Normal file
View File

@@ -0,0 +1,37 @@
// 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 functor
func MonadFlap[FAB ~func(A) B, A, B, HKTFAB, HKTB any](
fmap func(HKTFAB, func(FAB) B) HKTB,
fab HKTFAB,
a A,
) HKTB {
return fmap(fab, func(f FAB) B {
return f(a)
})
}
func Flap[FAB ~func(A) B, A, B, HKTFAB, HKTB any](
fmap func(HKTFAB, func(FAB) B) HKTB,
a A,
) func(HKTFAB) HKTB {
return func(fab HKTFAB) HKTB {
return MonadFlap(fmap, fab, a)
}
}

View File

@@ -20,6 +20,7 @@ import (
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain" C "github.com/IBM/fp-go/internal/chain"
FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/internal/lazy" L "github.com/IBM/fp-go/internal/lazy"
) )
@@ -143,3 +144,11 @@ func Defer[GA ~func() A, A any](gen func() GA) GA {
return gen()() return gen()()
}) })
} }
func MonadFlap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](fab GFAB, a A) GB {
return FC.MonadFlap(MonadMap[GFAB, GB, FAB, B], fab, a)
}
func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(GFAB) GB {
return F.Bind2nd(MonadFlap[FAB, GFAB, GB, A, B], a)
}

View File

@@ -84,7 +84,7 @@ func Flatten[A any](mma IO[IO[A]]) IO[A] {
return G.Flatten(mma) return G.Flatten(mma)
} }
// Memoize computes the value of the provided IO monad lazily but exactly once // Memoize computes the value of the provided [IO] monad lazily but exactly once
func Memoize[A any](ma IO[A]) IO[A] { func Memoize[A any](ma IO[A]) IO[A] {
return G.Memoize(ma) return G.Memoize(ma)
} }
@@ -138,3 +138,11 @@ var Now = G.Now[IO[time.Time]]()
func Defer[A any](gen func() IO[A]) IO[A] { func Defer[A any](gen func() IO[A]) IO[A] {
return G.Defer[IO[A]](gen) return G.Defer[IO[A]](gen)
} }
func MonadFlap[A, B any](fab IO[func(A) B], a A) IO[B] {
return G.MonadFlap[func(A) B, IO[func(A) B], IO[B], A, B](fab, a)
}
func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(IO[func(A) B]) IO[B] {
return G.Flap[func(A) B, IO[func(A) B], IO[B], A, B](a)
}

View File

@@ -16,6 +16,7 @@
package file package file
import ( import (
"io"
"os" "os"
IOE "github.com/IBM/fp-go/ioeither" IOE "github.com/IBM/fp-go/ioeither"
@@ -45,3 +46,10 @@ func Remove(name string) IOE.IOEither[error, string] {
return name, os.Remove(name) return name, os.Remove(name)
}) })
} }
// Close closes an object
func Close[C io.Closer](c C) IOE.IOEither[error, any] {
return IOE.TryCatchError(func() (any, error) {
return c, c.Close()
})
}

View File

@@ -31,7 +31,7 @@ func onReadAll[R io.Reader](r R) IOE.IOEither[error, []byte] {
func ReadAll[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, []byte] { func ReadAll[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, []byte] {
return IOE.WithResource[[]byte]( return IOE.WithResource[[]byte](
acquire, acquire,
onClose[R])( Close[R])(
onReadAll[R], onReadAll[R],
) )
} }

View File

@@ -38,7 +38,7 @@ func TestWithTempFileOnClosedFile(t *testing.T) {
return F.Pipe2( return F.Pipe2(
f, f,
onWriteAll[*os.File]([]byte("Carsten")), onWriteAll[*os.File]([]byte("Carsten")),
IOE.ChainFirst(F.Constant1[[]byte](onClose(f))), IOE.ChainFirst(F.Constant1[[]byte](Close(f))),
) )
}) })

View File

@@ -36,7 +36,7 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire IOE.IOEither[error, W]
return func(onCreate IOE.IOEither[error, W]) IOE.IOEither[error, []byte] { return func(onCreate IOE.IOEither[error, W]) IOE.IOEither[error, []byte] {
return IOE.WithResource[[]byte]( return IOE.WithResource[[]byte](
onCreate, onCreate,
onClose[W])( Close[W])(
onWrite, onWrite,
) )
} }
@@ -46,5 +46,5 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire IOE.IOEither[error, W]
func Write[R any, W io.WriteCloser](acquire IOE.IOEither[error, W]) func(use func(W) IOE.IOEither[error, R]) IOE.IOEither[error, R] { func Write[R any, W io.WriteCloser](acquire IOE.IOEither[error, W]) func(use func(W) IOE.IOEither[error, R]) IOE.IOEither[error, R] {
return IOE.WithResource[R]( return IOE.WithResource[R](
acquire, acquire,
onClose[W]) Close[W])
} }

View File

@@ -19,13 +19,13 @@ import (
S "github.com/IBM/fp-go/semigroup" S "github.com/IBM/fp-go/semigroup"
) )
func AlternativeMonoid[A, HKTA, HKTFA any]( func AlternativeMonoid[A, HKTA, HKTFA any, LAZYHKTA ~func() HKTA](
fof func(A) HKTA, fof func(A) HKTA,
fmap func(HKTA, func(A) func(A) A) HKTFA, fmap func(HKTA, func(A) func(A) A) HKTFA,
fap func(HKTFA, HKTA) HKTA, fap func(HKTFA, HKTA) HKTA,
falt func(HKTA, func() HKTA) HKTA, falt func(HKTA, LAZYHKTA) HKTA,
m Monoid[A], m Monoid[A],
@@ -45,9 +45,9 @@ func AlternativeMonoid[A, HKTA, HKTFA any](
) )
} }
func AltMonoid[HKTA any]( func AltMonoid[HKTA any, LAZYHKTA ~func() HKTA](
fzero func() HKTA, fzero LAZYHKTA,
falt func(HKTA, func() HKTA) HKTA, falt func(HKTA, LAZYHKTA) HKTA,
) Monoid[HKTA] { ) Monoid[HKTA] {

View File

@@ -51,3 +51,22 @@ func Monoid[A any]() func(S.Semigroup[A]) M.Monoid[Option[A]] {
return M.MakeMonoid(sg(s).Concat, None[A]()) return M.MakeMonoid(sg(s).Concat, None[A]())
} }
} }
// AlternativeMonoid is the alternative [Monoid] for an [Option]
func AlternativeMonoid[A any](m M.Monoid[A]) M.Monoid[Option[A]] {
return M.AlternativeMonoid(
Of[A],
MonadMap[A, func(A) A],
MonadAp[A, A],
MonadAlt[A],
m,
)
}
// AltMonoid is the alternative [Monoid] for an [Option]
func AltMonoid[A any]() M.Monoid[Option[A]] {
return M.AltMonoid(
None[A],
MonadAlt[A],
)
}

View File

@@ -18,6 +18,7 @@ package option
import ( import (
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
FC "github.com/IBM/fp-go/internal/functor"
) )
func fromPredicate[A any](a A, pred func(A) bool) Option[A] { func fromPredicate[A any](a A, pred func(A) bool) Option[A] {
@@ -115,6 +116,10 @@ func Flatten[A any](mma Option[Option[A]]) Option[A] {
return MonadChain(mma, F.Identity[Option[A]]) return MonadChain(mma, F.Identity[Option[A]])
} }
func MonadAlt[A any](fa Option[A], that func() Option[A]) Option[A] {
return MonadFold(fa, that, Of[A])
}
func Alt[A any](that func() Option[A]) func(Option[A]) Option[A] { func Alt[A any](that func() Option[A]) func(Option[A]) Option[A] {
return Fold(that, Of[A]) return Fold(that, Of[A])
} }
@@ -141,3 +146,11 @@ func Reduce[A, B any](f func(B, A) B, initial B) func(Option[A]) B {
func Filter[A any](pred func(A) bool) func(Option[A]) Option[A] { func Filter[A any](pred func(A) bool) func(Option[A]) Option[A] {
return Fold(None[A], F.Ternary(pred, Of[A], F.Ignore1of1[A](None[A]))) return Fold(None[A], F.Ternary(pred, Of[A], F.Ignore1of1[A](None[A])))
} }
func MonadFlap[A, B any](fab Option[func(A) B], a A) Option[B] {
return FC.MonadFlap(MonadMap[func(A) B, B], fab, a)
}
func Flap[A, B any](a A) func(Option[func(A) B]) Option[B] {
return F.Bind2nd(MonadFlap[A, B], a)
}

View File

@@ -16,7 +16,10 @@
package generic package generic
import ( import (
"sync"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
FIO "github.com/IBM/fp-go/internal/fromio"
FR "github.com/IBM/fp-go/internal/fromreader" FR "github.com/IBM/fp-go/internal/fromreader"
"github.com/IBM/fp-go/internal/readert" "github.com/IBM/fp-go/internal/readert"
IO "github.com/IBM/fp-go/io/generic" IO "github.com/IBM/fp-go/io/generic"
@@ -84,11 +87,37 @@ func Asks[GA ~func(E) A, GEA ~func(E) GIOA, GIOA ~func() A, E, A any](r GA) GEA
} }
func MonadChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GIOB) GEB { func MonadChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GIOB) GEB {
return MonadChain(ma, F.Flow2(f, FromIO[GEB])) return FIO.MonadChainIOK(
MonadChain[GEA, GEB],
FromIO[GEB],
ma, f,
)
} }
func ChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEB { func ChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEB {
return F.Bind2nd(MonadChainIOK[GEA, GEB, GIOA, GIOB, E, A, B], f) return FIO.ChainIOK(
MonadChain[GEA, GEB],
FromIO[GEB],
f,
)
}
func MonadChainFirstIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GIOB) GEA {
return FIO.MonadChainFirstIOK(
MonadChain[GEA, GEA],
MonadMap[GEB, GEA],
FromIO[GEB],
ma, f,
)
}
func ChainFirstIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEA {
return FIO.ChainFirstIOK(
MonadChain[GEA, GEA],
MonadMap[GEB, GEA],
FromIO[GEB],
f,
)
} }
// Defer creates an IO by creating a brand new IO via a generator function, each time // Defer creates an IO by creating a brand new IO via a generator function, each time
@@ -99,3 +128,30 @@ func Defer[GEA ~func(E) GA, GA ~func() A, E, A any](gen func() GEA) GEA {
} }
} }
} }
// Memoize computes the value of the provided reader monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[GEA ~func(E) GA, GA ~func() A, E, A any](rdr GEA) GEA {
// synchronization primitives
var once sync.Once
var result A
// callback
gen := func(e E) func() {
return func() {
result = rdr(e)()
}
}
// returns our memoized wrapper
return func(e E) GA {
io := gen(e)
return func() A {
once.Do(io)
return result
}
}
}
func Flatten[GEA ~func(R) GIOA, GGEA ~func(R) GIOEA, GIOA ~func() A, GIOEA ~func() GEA, R, A any](mma GGEA) GEA {
return MonadChain(mma, F.Identity[GEA])
}

View File

@@ -75,3 +75,14 @@ func ChainIOK[E, A, B any](f func(A) IO.IO[B]) func(ReaderIO[E, A]) ReaderIO[E,
func Defer[E, A any](gen func() ReaderIO[E, A]) ReaderIO[E, A] { func Defer[E, A any](gen func() ReaderIO[E, A]) ReaderIO[E, A] {
return G.Defer[ReaderIO[E, A]](gen) return G.Defer[ReaderIO[E, A]](gen)
} }
// Memoize computes the value of the provided [ReaderIO] monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[E, A any](rdr ReaderIO[E, A]) ReaderIO[E, A] {
return G.Memoize[ReaderIO[E, A]](rdr)
}
func Flatten[E, A any](mma ReaderIO[E, ReaderIO[E, A]]) ReaderIO[E, A] {
return G.Flatten[ReaderIO[E, A], ReaderIO[E, ReaderIO[E, A]]](mma)
}

View File

@@ -406,3 +406,11 @@ func TryCatch[GEA ~func(R) GA, GA ~func() ET.Either[E, A], R, E, A any](f func(R
return IOE.TryCatch[GA](f(r), onThrow) return IOE.TryCatch[GA](f(r), onThrow)
} }
} }
// Memoize computes the value of the provided monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[
GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](rdr GEA) GEA {
return G.Memoize[GEA](rdr)
}

View File

@@ -264,3 +264,11 @@ func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIO
func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] {
return G.Alt(second) return G.Alt(second)
} }
// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once
// The context used to compute the value is the context of the first call, so do not use this
// method if the value has a functional dependency on the content of the context
func Memoize[
R, E, A any](rdr ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] {
return G.Memoize[ReaderIOEither[R, E, A]](rdr)
}

View File

@@ -0,0 +1,65 @@
// 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 match
import (
"fmt"
E "github.com/IBM/fp-go/either"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
)
type Thing struct {
Name string
}
func (t Thing) GetName() string {
return t.Name
}
var (
// func(Thing) Either[error, string]
getName = F.Flow2(
Thing.GetName,
E.FromPredicate(S.IsNonEmpty, errors.OnSome[string]("value [%s] is empty")),
)
// func(option.Option[Thing]) Either[error, string]
GetName = F.Flow2(
E.FromOption[Thing](errors.OnNone("value is none")),
E.Chain(getName),
)
)
func ExampleEither_match() {
oThing := O.Of(Thing{"Carsten"})
res := F.Pipe2(
oThing,
GetName,
E.Fold(S.Format[error]("failed with error %v"), S.Format[string]("get value %s")),
)
fmt.Println(res)
// Output:
// get value Carsten
}

View File

@@ -15,8 +15,8 @@
package semigroup package semigroup
func AltSemigroup[HKTA any]( func AltSemigroup[HKTA any, LAZYHKTA ~func() HKTA](
falt func(HKTA, func() HKTA) HKTA, falt func(HKTA, LAZYHKTA) HKTA,
) Semigroup[HKTA] { ) Semigroup[HKTA] {