1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-26 19:38:58 +02:00

Compare commits

...

47 Commits

Author SHA1 Message Date
Carsten Leue
38120764e7 Merge pull request #40 from IBM/cleue-add-missing-lenses
fix: change Cache to Memoize and fix order of parameters to ToType
2023-09-10 21:37:38 +02:00
Dr. Carsten Leue
a83c2aec49 fix: change Cache to Memoize and fix order of parameters to ToType
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-10 21:29:01 +02:00
Carsten Leue
03debd37c8 Merge pull request #39 from IBM/cleue-implement-compact
fix: add compact methods
2023-09-08 22:57:18 +02:00
Dr. Carsten Leue
4f04344cda fix: add compact methods
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-08 22:54:58 +02:00
Carsten Leue
cf1c886f48 Merge pull request #37 from IBM/cleue-fix-order-of-generics-for-ChainOptionK
fix: order of generic parameters
2023-09-08 18:18:36 +02:00
Dr. Carsten Leue
3e0eb99b88 fix: order of generic parameters
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-08 18:16:06 +02:00
Carsten Leue
cb15a3d9fc Merge pull request #36 from IBM/cleue-mostly-adequate-more-samples
Cleue mostly adequate more samples
2023-09-08 15:31:01 +02:00
Dr. Carsten Leue
16535605f5 fix: add better sequence and traverse for option and either
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-08 15:26:56 +02:00
Dr. Carsten Leue
53f4e5ebd7 fix: add more mostly-adequate examples and solutions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-08 15:25:44 +02:00
Dr. Carsten Leue
fb91fd5dc8 fix: add more mostly-adequate examples and solutions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-07 17:21:39 +02:00
Carsten Leue
3ccafb5302 Merge pull request #35 from IBM/cleue-mostly-adequate-capter10
Add more examples for mostly-adequate
2023-09-06 15:15:00 +02:00
Dr. Carsten Leue
52b71ef4f3 fix: add more examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-05 22:51:46 +02:00
Dr. Carsten Leue
5d77d5bb3d fix: add sample app for rendering cat images
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-04 22:55:14 +02:00
Dr. Carsten Leue
8ba8f852fa Merge branch 'main' into cleue-mostly-adequate-capter10 2023-09-04 22:35:13 +02:00
Carsten Leue
29d9882d2a Merge pull request #34 from IBM/cleue-request-builder-example
doc: document how to use a request builder for http
2023-09-04 11:15:28 +02:00
Dr. Carsten Leue
f80ca31e14 doc: document how to use a request builder for http
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-04 11:13:58 +02:00
Dr. Carsten Leue
8692078972 doc: add examples and solutions from mostly adequate
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-03 22:21:23 +02:00
Dr. Carsten Leue
12a4f6801c fix: add ap sample
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-02 14:30:01 +02:00
Carsten Leue
8650a8a600 Merge pull request #33 from IBM/cleue-mostly-adequate-fp-go
fix: add examples for [Frisby's Mostly Adequate Guide]
2023-09-01 17:50:59 +02:00
Dr. Carsten Leue
fb3b1f115c fix: add examples for [Frisby's Mostly Adequate Guide] 2023-09-01 17:31:47 +02:00
Carsten Leue
ce66cf2295 Merge pull request #31 from IBM/cleue-add-cache
fix: implement simple cache for pure functions
2023-08-31 11:07:46 +02:00
Dr. Carsten Leue
80e579dd0b fix: implement simple cache for pure functions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-31 10:27:32 +02:00
Carsten Leue
ddafd1ee57 Merge pull request #30 from IBM/cleue-sample-ioeither
Add samples for IOEither
2023-08-29 09:16:17 +02:00
Dr. Carsten Leue
b5f077da71 fix: remove types from this branch
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-29 09:11:03 +02:00
Dr. Carsten Leue
1a0c40b419 fix: add helpers for reflect types
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-25 22:53:09 +02:00
Carsten Leue
d5d89b1853 fix: add runtime type validation
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-24 22:49:03 +02:00
Carsten Leue
0f061a5099 fix: initial implementation of types
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-22 22:34:05 +02:00
Carsten Leue
45e05f25ff Merge pull request #27 from jdbaldry/main-1
Fix a couple of spelling errors
2023-08-21 14:49:54 +02:00
Carsten Leue
a390d53451 add: bind to IOEither
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-18 22:15:26 +02:00
Jack Baldry
1346b9378a Fix a couple spelling errors 2023-08-18 09:45:27 +01:00
Carsten Leue
befd4f471e fix: add basic bind as do notation
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-17 23:03:03 +02:00
Carsten Leue
db8d3da87a Merge pull request #26 from IBM/cleue-more-samples
fix: add array examples
2023-08-11 22:32:26 +02:00
Dr. Carsten Leue
ee4e936183 fix: add array examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-11 22:31:36 +02:00
Carsten Leue
0064ac1c75 Merge pull request #25 from IBM/cleue-fix-build-break-1
fix: build break
2023-08-11 17:32:07 +02:00
Dr. Carsten Leue
8944a66c18 fix: build break
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-11 17:31:31 +02:00
Carsten Leue
bd0c42db01 Merge pull request #24 from IBM/cleue-add-disclaimer
fix: add more examples
2023-08-11 16:26:58 +02:00
Dr. Carsten Leue
e9f03e2d26 fix: add more examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-11 16:26:15 +02:00
Carsten Leue
bb630810fc Merge pull request #23 from IBM/cleue-better-docs
fix: experiment with docs and examples
2023-08-11 12:08:51 +02:00
Dr. Carsten Leue
9ba9eaacbe fix: experiment with docs and examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-11 11:56:11 +02:00
Carsten Leue
a9f6839acd Merge pull request #22 from IBM/cleue-add-FoldMap
fix: implement FoldMap
2023-08-10 18:08:48 +02:00
Dr. Carsten Leue
1b1dccc551 fix: implement FoldMap
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-10 18:08:11 +02:00
Carsten Leue
39c6108bf5 Merge pull request #21 from IBM/cleue-fix-buildbreak
fix: build break
2023-08-10 13:24:28 +02:00
Dr. Carsten Leue
83e1ff30c1 chore: test on multiple versions of go
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-10 13:22:31 +02:00
Dr. Carsten Leue
d9dda4cfa5 fix: build break
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-10 13:14:48 +02:00
Carsten Leue
d9b2804a7e Merge pull request #20 from IBM/cleue-add-FilterChain-operations
fix: implement FilterChain
2023-08-10 11:47:07 +02:00
Dr. Carsten Leue
c0028918ae fix: implement FilterChain
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-10 11:46:30 +02:00
Carsten Leue
cd53cb7036 Merge pull request #19 from IBM/cleue-implement-first-and-last
fix: add first and last
2023-08-07 22:36:12 +02:00
125 changed files with 3944 additions and 431 deletions

View File

@@ -17,22 +17,25 @@ on:
env:
# Currently no way to detect automatically
DEFAULT_BRANCH: main
GO_VERSION: 1.20.5 # renovate: datasource=golang-version depName=golang
GO_VERSION: 1.20.6 # renovate: datasource=golang-version depName=golang
NODE_VERSION: 18
DRY_RUN: true
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x' ]
steps:
# full checkout for semantic-release
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
with:
fetch-depth: 0
- name: Set up go ${{env.GO_VERSION}}
- name: Set up go ${{ matrix.go-version }}
uses: actions/setup-go@v4
with:
go-version: ${{env.GO_VERSION}}
go-version: ${{ matrix.go-version }}
-
name: Tests
run: |

View File

@@ -73,7 +73,7 @@ This library aims to provide a set of data types and functions that make it easy
The library itself also comprises many small functions, but it's admittedly harder to maintain than code that uses it. However this asymmetry is intended because it offloads complexity from users into a central component.
## Comparation to Idiomatic Go
## Comparison to Idiomatic Go
In this section we discuss how the functional APIs differ from idiomatic go function signatures and how to convert back and forth.
@@ -181,7 +181,7 @@ The `Map` operation for `ReaderIOEither` is defined as:
func Map[R, E, A, B any](f func(A) B) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B]
```
and in fact the equivalent operations for all other mondas follow the same pattern, we could try to introduce a new type for `ReaderIOEither` (without a parameter) as a HKT, e.g. like so (made-up syntax, does not work in go):
and in fact the equivalent operations for all other monads follow the same pattern, we could try to introduce a new type for `ReaderIOEither` (without a parameter) as a HKT, e.g. like so (made-up syntax, does not work in go):
```go
func Map[HKT, R, E, A, B any](f func(A) B) func(HKT[R, E, A]) HKT[R, E, B]

View File

@@ -108,10 +108,16 @@ func MonadFilterMap[A, B any](fa []A, f func(a A) O.Option[B]) []B {
return G.MonadFilterMap[[]A, []B](fa, f)
}
// FilterChain 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 A) O.Option[B]) func([]A) []B {
return G.FilterMap[[]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)
@@ -237,7 +243,7 @@ func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
}
func Flatten[A any](mma [][]A) []A {
return MonadChain(mma, F.Identity[[]A])
return G.Flatten(mma)
}
func Slice[A any](low, high int) func(as []A) []A {
@@ -288,3 +294,17 @@ func SliceRight[A any](start int) func([]A) []A {
func Copy[A any](b []A) []A {
return G.Copy(b)
}
// 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)
}
// 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) func([]A) []A {
return G.Push[[]A](a)
}

View File

@@ -16,6 +16,7 @@
package array
import (
"fmt"
"strings"
"testing"
@@ -142,3 +143,52 @@ func TestPartition(t *testing.T) {
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
}

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/function"
O "github.com/IBM/fp-go/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/function"
I "github.com/IBM/fp-go/number/integer"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/ord"
S "github.com/IBM/fp-go/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}}]
}

View File

@@ -18,6 +18,7 @@ package generic
import (
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/array"
M "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/tuple"
)
@@ -27,6 +28,14 @@ func Of[GA ~[]A, A any](value A) GA {
return GA{value}
}
func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
return array.Reduce(fa, f, initial)
}
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
return array.ReduceWithIndex(fa, f, initial)
}
// From constructs an array from a set of variadic arguments
func From[GA ~[]A, A any](data ...A) GA {
return data
@@ -104,6 +113,10 @@ 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 F.Bind2nd(MonadMap[GA, GB, A, B], f)
}
func Size[GA ~[]A, A any](as GA) int {
return len(as)
}
@@ -118,6 +131,17 @@ func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B])
return filterMap[GA, GB](fa, f)
}
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 A) O.Option[B]) func(GA) GB {
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
}
@@ -198,3 +222,23 @@ func Copy[AS ~[]A, A any](b AS) AS {
copy(buf, b)
return buf
}
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 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[GA ~[]A, A any](a A) func(GA) GA {
return F.Bind2nd(array.Push[GA, A], a)
}

View File

@@ -18,11 +18,17 @@ package generic
import (
"sort"
F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/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
@@ -34,8 +40,17 @@ func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
cpy := make(GA, l)
copy(cpy, ma)
sort.Slice(cpy, func(i, j int) bool {
return ord.Compare(cpy[i], cpy[j]) < 0
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],
)
}

View File

@@ -24,3 +24,13 @@ import (
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)
}

View File

@@ -388,7 +388,7 @@ func generateApplyHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -266,7 +266,7 @@ func generateBindHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n", pkg)

View File

@@ -150,7 +150,7 @@ func generateEitherHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -27,5 +27,5 @@ func writePackage(f *os.File, pkg string) {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
}

View File

@@ -62,7 +62,7 @@ func generateIdentityHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -81,7 +81,7 @@ func generateIOHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -217,7 +217,7 @@ func generateIOEitherHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -81,7 +81,7 @@ func generateIOOptionHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -148,7 +148,7 @@ func generateOptionHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -337,7 +337,7 @@ func generatePipeHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n", pkg)

View File

@@ -12,6 +12,7 @@
// 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 (
@@ -117,7 +118,7 @@ func generateReaderHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -169,7 +169,7 @@ func generateReaderIOEitherHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)

View File

@@ -20,6 +20,7 @@ import (
"log"
"os"
"path/filepath"
"strings"
"time"
C "github.com/urfave/cli/v2"
@@ -36,8 +37,51 @@ func writeTupleType(f *os.File, symbol string, i int) {
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 optionize version
// 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++ {
@@ -355,7 +399,7 @@ func generateTupleHelpers(filename string, count int) error {
// 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", time.Now())
fmt.Fprintf(f, "// %s\n\n", time.Now())
fmt.Fprintf(f, "package %s\n\n", pkg)
@@ -398,6 +442,10 @@ import (
generateToArray(f, i)
// generate fromArray
generateFromArray(f, i)
// generate push
if i < count {
generatePush(f, i)
}
}
return nil

View File

@@ -1,23 +1,8 @@
// 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
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:20.012425 +0200 CEST m=+0.019517901
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
import (
"context"

View File

@@ -1,23 +1,8 @@
// 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
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:20.012425 +0200 CEST m=+0.019517901
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
import (
"context"

View File

@@ -21,6 +21,13 @@ import (
"testing"
H "net/http"
R "github.com/IBM/fp-go/context/readerioeither"
E "github.com/IBM/fp-go/either"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
IOE "github.com/IBM/fp-go/ioeither"
"github.com/stretchr/testify/assert"
)
type PostItem struct {
@@ -30,6 +37,47 @@ type PostItem struct {
Body string `json:"body"`
}
func getTitle(item PostItem) string {
return item.Title
}
type simpleRequestBuilder struct {
method string
url string
headers H.Header
}
func requestBuilder() simpleRequestBuilder {
return simpleRequestBuilder{method: "GET"}
}
func (b simpleRequestBuilder) WithURL(url string) simpleRequestBuilder {
b.url = url
return b
}
func (b simpleRequestBuilder) WithHeader(key, value string) simpleRequestBuilder {
if b.headers == nil {
b.headers = make(H.Header)
} else {
b.headers = b.headers.Clone()
}
b.headers.Set(key, value)
return b
}
func (b simpleRequestBuilder) Build() R.ReaderIOEither[*H.Request] {
return func(ctx context.Context) IOE.IOEither[error, *H.Request] {
return IOE.TryCatchError(func() (*H.Request, error) {
req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil)
if err == nil {
req.Header = b.headers
}
return req, err
})
}
}
func TestSendSingleRequest(t *testing.T) {
client := MakeClient(H.DefaultClient)
@@ -40,7 +88,70 @@ func TestSendSingleRequest(t *testing.T) {
resp1 := readItem(req1)
resE := resp1(context.Background())()
resE := resp1(context.TODO())()
fmt.Println(resE)
}
// setHeaderUnsafe updates a header value in a request object by mutating the request object
func setHeaderUnsafe(key, value string) func(*H.Request) *H.Request {
return func(req *H.Request) *H.Request {
req.Header.Set(key, value)
return req
}
}
func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) {
client := MakeClient(H.DefaultClient)
// this is not safe from a puristic perspective, because the map call mutates the request object
req1 := F.Pipe2(
"https://jsonplaceholder.typicode.com/posts/1",
MakeGetRequest,
R.Map(setHeaderUnsafe("Content-Type", "text/html")),
)
readItem := ReadJson[PostItem](client)
resp1 := F.Pipe2(
req1,
readItem,
R.Map(getTitle),
)
res := F.Pipe1(
resp1(context.TODO())(),
E.GetOrElse(errors.ToString),
)
assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res)
}
func TestSendSingleRequestWithHeaderSafe(t *testing.T) {
client := MakeClient(H.DefaultClient)
// the request builder assembles config values to construct
// the final http request. Each `With` step creates a copy of the settings
// so the flow is pure
request := requestBuilder().
WithURL("https://jsonplaceholder.typicode.com/posts/1").
WithHeader("Content-Type", "text/html").
Build()
readItem := ReadJson[PostItem](client)
response := F.Pipe2(
request,
readItem,
R.Map(getTitle),
)
res := F.Pipe1(
response(context.TODO())(),
E.GetOrElse(errors.ToString),
)
assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res)
}

View File

@@ -44,3 +44,15 @@ func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA
func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] {
return SequenceArrayG[[]A](ma)
}
// CompactArrayG discards the none values and keeps the some values
func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 {
return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 {
return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out))
}, make(A2, len(fa)))
}
// CompactArray discards the none values and keeps the some values
func CompactArray[E, A any](fa []Either[E, A]) []A {
return CompactArrayG[[]Either[E, A], []A](fa)
}

View File

@@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package option defines the [Either] datastructure and its monadic operations
package either
//go:generate go run .. either --count 10 --filename gen.go

View File

@@ -91,11 +91,11 @@ func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
return mb
}
func MonadChainOptionK[E, A, B 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)))
}
func ChainOptionK[E, A, B 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)
return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
return Chain(F.Flow2(f, from))
@@ -146,8 +146,8 @@ func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] {
return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A])
}
func ToOption[E, A any]() func(Either[E, A]) O.Option[A] {
return Fold(F.Ignore1of1[E](O.None[A]), O.Some[A])
func ToOption[E, A any](ma Either[E, A]) O.Option[A] {
return MonadFold(ma, F.Ignore1of1[E](O.None[A]), O.Some[A])
}
func FromError[A any](f func(a A) error) func(A) Either[error, A] {
@@ -182,7 +182,7 @@ func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Eithe
}
}
func FromNillable[E, A any](e E) func(*A) Either[E, *A] {
func FromNillable[A, E any](e E) func(*A) Either[E, *A] {
return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e))
}
@@ -209,7 +209,7 @@ func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E
return Fold(onLeft, Of[E, A])
}
func ToType[E, A any](onError func(any) E) func(any) Either[E, A] {
func ToType[A, E any](onError func(any) E) func(any) Either[E, A] {
return func(value any) Either[E, A] {
return F.Pipe2(
value,

View File

@@ -100,7 +100,7 @@ func TestChainFirst(t *testing.T) {
}
func TestChainOptionK(t *testing.T) {
f := ChainOptionK[string, int, int](F.Constant("a"))(func(n int) O.Option[int] {
f := ChainOptionK[int, int](F.Constant("a"))(func(n int) O.Option[int] {
if n > 0 {
return O.Some(n)
}

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 either
import (
"fmt"
"github.com/IBM/fp-go/errors"
)
func ExampleEither_creation() {
// Build an Either
leftValue := Left[string](fmt.Errorf("some error"))
rightValue := Right[error]("value")
// Build from a value
fromNillable := FromNillable[string](fmt.Errorf("value was nil"))
leftFromNil := fromNillable(nil)
value := "value"
rightFromPointer := fromNillable(&value)
// some predicate
isEven := func(num int) bool {
return num%2 == 0
}
fromEven := FromPredicate(isEven, errors.OnSome[int]("%d is an odd number"))
leftFromPred := fromEven(3)
rightFromPred := fromEven(4)
fmt.Println(leftValue)
fmt.Println(rightValue)
fmt.Println(leftFromNil)
fmt.Println(IsRight(rightFromPointer))
fmt.Println(leftFromPred)
fmt.Println(rightFromPred)
// Output:
// Left[*errors.errorString, string](some error)
// Right[<nil>, string](value)
// Left[*errors.errorString, *string](value was nil)
// true
// Left[*errors.errorString, int](3 is an odd number)
// Right[<nil>, int](4)
}

View File

@@ -0,0 +1,64 @@
// 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 (
"fmt"
F "github.com/IBM/fp-go/function"
N "github.com/IBM/fp-go/number"
)
func ExampleEither_extraction() {
leftValue := Left[int](fmt.Errorf("Division by Zero!"))
rightValue := Right[error](10)
// Convert Either[E, A] to A with a default value
leftWithDefault := GetOrElse(F.Constant1[error](0))(leftValue) // 0
rightWithDefault := GetOrElse(F.Constant1[error](0))(rightValue) // 10
// Apply a different function on Left(...)/Right(...)
doubleOrZero := Fold(F.Constant1[error](0), N.Mul(2)) // func(Either[error, int]) int
doubleFromLeft := doubleOrZero(leftValue) // 0
doubleFromRight := doubleOrZero(rightValue) // 20
// Pro-tip: Fold is short for the following:
doubleOrZeroBis := F.Flow2(
Map[error](N.Mul(2)),
GetOrElse(F.Constant1[error](0)),
)
doubleFromLeftBis := doubleOrZeroBis(leftValue) // 0
doubleFromRightBis := doubleOrZeroBis(rightValue) // 20
fmt.Println(leftValue)
fmt.Println(rightValue)
fmt.Println(leftWithDefault)
fmt.Println(rightWithDefault)
fmt.Println(doubleFromLeft)
fmt.Println(doubleFromRight)
fmt.Println(doubleFromLeftBis)
fmt.Println(doubleFromRightBis)
// Output:
// Left[*errors.errorString, int](Division by Zero!)
// Right[<nil>, int](10)
// 0
// 10
// 0
// 20
// 0
// 20
}

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:23.165015 +0200 CEST m=+0.038998801
// 2023-08-17 22:58:57.9925506 +0200 CEST m=+0.033401101
package either
import (

View File

@@ -43,3 +43,24 @@ func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A an
func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] {
return SequenceRecordG[map[K]A](ma)
}
func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M {
r[k] = v
return r
}
// CompactRecordG discards the noe values and keeps the some values
func CompactRecordG[M1 ~map[K]Either[E, A], M2 ~map[K]A, K comparable, E, A any](m M1) M2 {
out := make(M2)
onLeft := F.Constant1[E](out)
return RR.ReduceWithIndex(m, func(key K, _ M2, value Either[E, A]) M2 {
return MonadFold(value, onLeft, func(v A) M2 {
return upsertAtReadWrite(out, key, v)
})
}, out)
}
// CompactRecord discards all none values and keeps the somes
func CompactRecord[K comparable, E, A any](m map[K]Either[E, A]) map[K]A {
return CompactRecordG[map[K]Either[E, A], map[K]A](m)
}

37
either/record_test.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 either
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCompactRecord(t *testing.T) {
// make the map
m := make(map[string]Either[string, int])
m["foo"] = Left[int]("error")
m["bar"] = Right[string](1)
// compact it
m1 := CompactRecord(m)
// check expected
exp := map[string]int{
"bar": 1,
}
assert.Equal(t, exp, m1)
}

View File

@@ -27,13 +27,13 @@ HKTRB = HKT<Either[B]>
HKTA = HKT<A>
HKTB = HKT<B>
*/
func traverse[E, A, B, HKTA, HKTB, HKTRB any](
_of func(Either[E, B]) HKTRB,
_map func(HKTB, func(B) Either[E, B]) HKTRB,
func traverse[E, A, B, HKTB, HKTRB any](
mof func(Either[E, B]) HKTRB,
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
) func(Either[E, A], func(A) HKTB) HKTRB {
left := F.Flow2(Left[B, E], _of)
right := F.Bind2nd(_map, Right[E, B])
left := F.Flow2(Left[B, E], mof)
right := mmap(Right[E, B])
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
return MonadFold(ta,
@@ -43,27 +43,21 @@ func traverse[E, A, B, HKTA, HKTB, HKTRB any](
}
}
func Traverse[E, A, B, HKTA, HKTB, HKTRB any](
_of func(Either[E, B]) HKTRB,
_map func(HKTB, func(B) Either[E, B]) HKTRB,
// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
func Traverse[A, E, B, HKTB, HKTRB any](
mof func(Either[E, B]) HKTRB,
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
delegate := traverse[E, A, B, HKTA](_of, _map)
delegate := traverse[E, A, B](mof, mmap)
return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
return F.Bind2nd(delegate, f)
}
}
/*
*
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
HKTRA = HKT<Either[A]>
HKTA = HKT<A>
HKTB = HKT<B>
*/
// Sequence converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
func Sequence[E, A, HKTA, HKTRA any](
_of func(Either[E, A]) HKTRA,
_map func(HKTA, func(A) Either[E, A]) HKTRA,
mof func(Either[E, A]) HKTRA,
mmap func(func(A) Either[E, A]) func(HKTA) HKTRA,
) func(Either[E, HKTA]) HKTRA {
return Fold(F.Flow2(Left[A, E], _of), F.Bind2nd(_map, Right[E, A]))
return Fold(F.Flow2(Left[A, E], mof), mmap(Right[E, A]))
}

View File

@@ -30,9 +30,9 @@ func TestTraverse(t *testing.T) {
}
return O.None[int]()
}
trav := Traverse[string, int, int, O.Option[Either[string, int]]](
trav := Traverse[int](
O.Of[Either[string, int]],
O.MonadMap[int, Either[string, int]],
O.Map[int, Either[string, int]],
)(f)
assert.Equal(t, O.Of(Left[int]("a")), F.Pipe1(Left[int]("a"), trav))
@@ -44,7 +44,7 @@ func TestSequence(t *testing.T) {
seq := Sequence(
O.Of[Either[string, int]],
O.MonadMap[int, Either[string, int]],
O.Map[int, Either[string, int]],
)
assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1))))

View File

@@ -21,16 +21,12 @@ import (
S "github.com/IBM/fp-go/semigroup"
)
func concat[A any](first, second func(A) A) func(A) A {
return F.Flow2(first, second)
}
// Semigroup for the Endomorphism where the `concat` operation is the usual function composition.
func Semigroup[A any]() S.Semigroup[func(A) A] {
return S.MakeSemigroup(concat[A])
return S.MakeSemigroup(F.Flow2[func(A) A, func(A) A])
}
// Monoid for the Endomorphism where the `concat` operation is the usual function composition.
func Monoid[A any]() M.Monoid[func(A) A] {
return M.MakeMonoid(concat[A], F.Identity[A])
return M.MakeMonoid(F.Flow2[func(A) A, func(A) A], F.Identity[A])
}

View File

@@ -28,6 +28,22 @@ func OnNone(msg string, args ...any) func() error {
}
}
// OnSome generates a unary function that produces a formatted error
func OnSome[T any](msg string, args ...any) func(T) error {
l := len(args)
if l == 0 {
return func(value T) error {
return fmt.Errorf(msg, value)
}
}
return func(value T) error {
data := make([]any, l)
copy(data[1:], args)
data[0] = value
return fmt.Errorf(msg, data...)
}
}
// OnError generates a unary function that produces a formatted error. The argument
// to that function is the root cause of the error and the message will be augmented with
// a format string containing %w

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:40.161663 +0200 CEST m=+0.009458101
// 2023-08-17 22:59:01.9404821 +0200 CEST m=+0.161366801
package function
// Combinations for a total of 1 arguments

30
function/cache.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 function
import (
G "github.com/IBM/fp-go/function/generic"
)
// Memoize converts a unary function into a unary function that caches the value depending on the parameter
func Memoize[K comparable, T any](f func(K) T) func(K) T {
return G.Memoize(f)
}
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
func ContramapMemoize[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T {
return G.ContramapMemoize[func(A) T](kf)
}

50
function/cache_test.go Normal file
View File

@@ -0,0 +1,50 @@
// 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 function
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCache(t *testing.T) {
var count int
withSideEffect := func(n int) int {
count++
return n
}
cached := Memoize(withSideEffect)
assert.Equal(t, 0, count)
assert.Equal(t, 10, cached(10))
assert.Equal(t, 1, count)
assert.Equal(t, 10, cached(10))
assert.Equal(t, 1, count)
assert.Equal(t, 20, cached(20))
assert.Equal(t, 2, count)
assert.Equal(t, 20, cached(20))
assert.Equal(t, 2, count)
assert.Equal(t, 10, cached(10))
assert.Equal(t, 2, count)
}

View File

@@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package function implements function composition primitives, most prominently [Pipe2] and [Flow2]
package function
//go:generate go run .. pipe --count 20 --filename gen.go

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:27.8029852 +0200 CEST m=+0.051432001
// 2023-08-17 22:58:59.8663985 +0200 CEST m=+0.103744101
package function
// Pipe0 takes an initial value t0 and successively applies 0 functions where the input of a function is the return value of the previous function

65
function/generic/cache.go Normal file
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 generic
import (
"sync"
L "github.com/IBM/fp-go/internal/lazy"
)
// Memoize converts a unary function into a unary function that caches the value depending on the parameter
func Memoize[F ~func(K) T, K comparable, T any](f F) F {
return ContramapMemoize[F](func(k K) K { return k })(f)
}
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
return CacheCallback[F](kf, getOrCreate[K, T]())
}
// getOrCreate is a naive implementation of a cache, without bounds
func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
cache := make(map[K]func() T)
var l sync.Mutex
return func(k K, cb func() func() T) func() T {
// only lock to access a lazy accessor to the value
l.Lock()
existing, ok := cache[k]
if !ok {
existing = cb()
cache[k] = existing
}
l.Unlock()
// compute the value outside of the lock
return existing
}
}
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
func CacheCallback[F ~func(A) T, KF func(A) K, C ~func(K, func() func() T) func() T, A any, K comparable, T any](kf KF, getOrCreate C) func(F) F {
return func(f F) F {
return func(a A) T {
// cache entry
return getOrCreate(kf(a), func() func() T {
return L.Memoize[func() T](func() T {
return f(a)
})
})()
}
}
}

62
function/pipe_test.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 function
import (
"fmt"
)
func addSthg(value int) int {
return value + 1
}
func doSthgElse(value int) int {
return value * 2
}
func doFinalSthg(value int) string {
return fmt.Sprintf("final value: %d", value)
}
func Example() {
// start point
value := 1
// imperative style
value1 := addSthg(value) // 2
value2 := doSthgElse(value1) // 4
finalValueImperative := doFinalSthg(value2) // "final value: 4"
// the same but inline
finalValueInline := doFinalSthg(doSthgElse(addSthg(value)))
// with pipe
finalValuePipe := Pipe3(value, addSthg, doSthgElse, doFinalSthg)
// with flow
transform := Flow3(addSthg, doSthgElse, doFinalSthg)
finalValueFlow := transform(value)
fmt.Println(finalValueImperative)
fmt.Println(finalValueInline)
fmt.Println(finalValuePipe)
fmt.Println(finalValueFlow)
// Output:
// final value: 4
// final value: 4
// final value: 4
// final value: 4
}

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:46.7920356 +0200 CEST m=+0.045177201
// 2023-08-17 22:59:06.2987136 +0200 CEST m=+0.049156901
package identity
import (

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:49.4210571 +0200 CEST m=+0.040316501
// 2023-08-17 22:59:08.1313362 +0200 CEST m=+0.111171801
package apply
import (

View File

@@ -42,10 +42,27 @@ func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
return current
}
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
current := initial
count := len(fa)
for i := 0; i < count; i++ {
current = f(i, current, fa[i])
}
return current
}
func Append[GA ~[]A, A any](as GA, a A) GA {
return append(as, a)
}
func Push[GA ~[]A, A any](as GA, a A) GA {
l := len(as)
cpy := make(GA, l+1)
copy(cpy, as)
cpy[l] = a
return cpy
}
func Empty[GA ~[]A, A any]() GA {
return make(GA, 0)
}

75
internal/bindt/bind.go Normal file
View File

@@ -0,0 +1,75 @@
// 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 bindt
import (
F "github.com/IBM/fp-go/function"
I "github.com/IBM/fp-go/identity"
T "github.com/IBM/fp-go/tuple"
)
func Bind[SET ~func(B) func(S1) S2, FCT ~func(S1) HKTB, S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(func(S1) HKTS2) func(HKTS1) HKTS2,
mmap func(func(B) S2) func(HKTB) HKTS2,
s SET,
f FCT,
) func(HKTS1) HKTS2 {
return mchain(F.Flow3(
T.Replicate2[S1],
T.Map2(F.Flow2(
I.Ap[S2, S1],
F.Flow2(
F.Bind1st(F.Flow2[SET, func(func(S1) S2) S2], s),
mmap,
)), f),
T.Tupled2(I.MonadAp[HKTS2, HKTB]),
))
}
func BindTo[SET ~func(B) S2, S2, B, HKTS2, HKTB any](
mmap func(func(B) S2) func(HKTB) HKTS2,
s SET,
) func(HKTB) HKTS2 {
return mmap(s)
}
func ApS[
SET ~func(B) func(S1) S2,
S1, S2, B, HKTS1S2, HKTS1, HKTS2, HKTB any,
](
ap func(HKTS1) func(HKTS1S2) HKTS2,
mmap func(func(B) func(S1) S2) func(HKTB) HKTS1S2,
s SET, fb HKTB) func(HKTS1) HKTS2 {
return F.Flow2(
ap,
I.Ap[HKTS2, HKTS1S2](mmap(s)(fb)),
)
}
func Let[SET ~func(B) func(S1) S2, FCT ~func(S1) B, S1, S2, B, HKTS1, HKTS2 any](
mmap func(func(S1) S2) func(HKTS1) HKTS2,
s SET,
f FCT,
) func(HKTS1) HKTS2 {
return mmap(F.Flow3(
T.Replicate2[S1],
T.Map2(F.Flow2(
I.Ap[S2, S1],
F.Bind1st(F.Flow2[SET, func(func(S1) S2) S2], s)), f),
T.Tupled2(I.MonadAp[S2, B]),
))
}

34
internal/lazy/memoize.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 lazy
import "sync"
// Memoize computes the value of the provided IO monad lazily but exactly once
func Memoize[GA ~func() A, A any](ma GA) GA {
// synchronization primitives
var once sync.Once
var result A
// callback
gen := func() {
result = ma()
}
// returns our memoized wrapper
return func() A {
once.Do(gen)
return result
}
}

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:57.7378739 +0200 CEST m=+0.063371401
// 2023-08-17 22:59:10.1040409 +0200 CEST m=+0.089470201
package io
import (

View File

@@ -64,6 +64,28 @@ func MonadApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](f
)
}
// MonadApFirstPar combines two effectful actions, keeping only the result of the first.
func MonadApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA {
return G.MonadApFirst(
MonadApPar[GB, GA, GBA, B, A],
MonadMap[GA, GBA, A, func(B) A],
first,
second,
)
}
// MonadApFirstSeq combines two effectful actions, keeping only the result of the first.
func MonadApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA {
return G.MonadApFirst(
MonadApSeq[GB, GA, GBA, B, A],
MonadMap[GA, GBA, A, func(B) A],
first,
second,
)
}
// ApFirst combines two effectful actions, keeping only the result of the first.
func ApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA {
return G.ApFirst(
@@ -74,6 +96,26 @@ func ApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second
)
}
// ApFirstPar combines two effectful actions, keeping only the result of the first.
func ApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA {
return G.ApFirst(
MonadApPar[GB, GA, GBA, B, A],
MonadMap[GA, GBA, A, func(B) A],
second,
)
}
// ApFirstSeq combines two effectful actions, keeping only the result of the first.
func ApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA {
return G.ApFirst(
MonadApSeq[GB, GA, GBA, B, A],
MonadMap[GA, GBA, A, func(B) A],
second,
)
}
// MonadApSecond combines two effectful actions, keeping only the result of the second.
func MonadApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](first GA, second GB) GB {
return G.MonadApSecond(

View File

@@ -1,21 +1,6 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:48:57.7499229 +0200 CEST m=+0.075420401
// 2023-08-17 22:59:10.1132645 +0200 CEST m=+0.098693801
package generic
import (

View File

@@ -16,11 +16,11 @@
package generic
import (
"sync"
"time"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain"
L "github.com/IBM/fp-go/internal/lazy"
)
// type IO[A any] = func() A
@@ -119,18 +119,7 @@ func Flatten[GA ~func() A, GAA ~func() GA, A any](mma GAA) GA {
// Memoize computes the value of the provided IO monad lazily but exactly once
func Memoize[GA ~func() A, A any](ma GA) GA {
// synchronization primitives
var once sync.Once
var result A
// callback
gen := func() {
result = ma()
}
// returns our memoized wrapper
return func() A {
once.Do(gen)
return result
}
return L.Memoize[GA, A](ma)
}
// Delay creates an operation that passes in the value after some delay

44
ioeither/bind.go Normal file
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 ioeither
import (
G "github.com/IBM/fp-go/ioeither/generic"
)
// Bind applies a function to an input state and merges the result into that state
func Bind[E, A, S1, S2 any](s func(A) func(S1) S2, f func(S1) IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], func(S1) IOEither[E, A]](s, f)
}
// BindTo initializes some state based on a value
func BindTo[
E, A, S2 any](s func(A) S2) func(IOEither[E, A]) IOEither[E, S2] {
return G.BindTo[IOEither[E, S2], IOEither[E, A]](s)
}
func ApS[
E, A, S1, S2 any,
](s func(A) func(S1) S2, fa IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], IOEither[E, func(S1) S2]](s, fa)
}
func Let[E, A, S1, S2 any](
s func(A) func(S1) S2,
f func(S1) A,
) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Let[IOEither[E, S1], IOEither[E, S2]](s, f)
}

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 ioeither
import (
"fmt"
E "github.com/IBM/fp-go/either"
)
func ExampleIOEither_creation() {
// Build an IOEither
leftValue := Left[string](fmt.Errorf("some error"))
rightValue := Right[error]("value")
// Convert from Either
eitherValue := E.Of[error](42)
ioFromEither := FromEither(eitherValue)
// some predicate
isEven := func(num int) (int, error) {
if num%2 == 0 {
return num, nil
}
return 0, fmt.Errorf("%d is an odd number", num)
}
fromEven := Eitherize1(isEven)
leftFromPred := fromEven(3)
rightFromPred := fromEven(4)
fmt.Println(leftValue())
fmt.Println(rightValue())
fmt.Println(ioFromEither())
fmt.Println(leftFromPred())
fmt.Println(rightFromPred())
// Output:
// Left[*errors.errorString, string](some error)
// Right[<nil>, string](value)
// Right[<nil>, int](42)
// Left[*errors.errorString, int](3 is an odd number)
// Right[<nil>, int](4)
}

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 ioeither
import (
"fmt"
"log"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
T "github.com/IBM/fp-go/tuple"
)
func ExampleIOEither_do() {
foo := Of[error]("foo")
bar := Of[error](1)
// quux consumes the state of three bindings and returns an [IO] instead of an [IOEither]
quux := func(t T.Tuple3[string, int, string]) IO.IO[any] {
return IO.FromImpure(func() {
log.Printf("t1: %s, t2: %d, t3: %s", t.F1, t.F2, t.F3)
})
}
transform := func(t T.Tuple3[string, int, string]) int {
return len(t.F1) + t.F2 + len(t.F3)
}
b := F.Pipe5(
foo,
BindTo[error](T.Of[string]),
ApS(T.Push1[string, int], bar),
Bind(T.Push2[string, int, string], func(t T.Tuple2[string, int]) IOEither[error, string] {
return Of[error](fmt.Sprintf("%s%d", t.F1, t.F2))
}),
ChainFirstIOK[error](quux),
Map[error](transform),
)
fmt.Println(b())
// Output:
// Right[<nil>, int](8)
}

View File

@@ -0,0 +1,45 @@
// 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 (
"fmt"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
)
func ExampleIOEither_extraction() {
// IOEither
someIOEither := Right[error](42)
eitherValue := someIOEither() // E.Right(42)
value := E.GetOrElse(F.Constant1[error](0))(eitherValue) // 42
// Or more directly
infaillibleIO := GetOrElse(F.Constant1[error](IO.Of(0)))(someIOEither) // => IO.Right(42)
valueFromIO := infaillibleIO() // => 42
fmt.Println(eitherValue)
fmt.Println(value)
fmt.Println(valueFromIO)
// Output:
// Right[<nil>, int](42)
// 42
// 42
}

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:00.7618943 +0200 CEST m=+0.065441701
// 2023-08-17 22:59:12.0033119 +0200 CEST m=+0.096740401
package ioeither
import (

79
ioeither/generic/bind.go Normal file
View File

@@ -0,0 +1,79 @@
// 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 (
ET "github.com/IBM/fp-go/either"
G "github.com/IBM/fp-go/internal/bindt"
)
// Bind applies a function to an input state and merges the result into that state
func Bind[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
GA ~func() ET.Either[E, A],
FCT ~func(S1) GA,
E any,
SET ~func(A) func(S1) S2,
A, S1, S2 any](s SET, f FCT) func(GS1) GS2 {
return G.Bind(
Chain[GS1, GS2, E, S1, S2],
Map[GA, GS2, E, A, S2],
s,
f,
)
}
// BindTo initializes some state based on a value
func BindTo[
GS2 ~func() ET.Either[E, S2],
GA ~func() ET.Either[E, A],
E any,
SET ~func(A) S2,
A, S2 any](s SET) func(GA) GS2 {
return G.BindTo(
Map[GA, GS2, E, A, S2],
s,
)
}
func ApS[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
GB ~func() ET.Either[E, B],
GS1S2 ~func() ET.Either[E, func(S1) S2],
SET ~func(B) func(S1) S2,
E, S1, S2, B any,
](s SET, fb GB) func(GS1) GS2 {
return G.ApS[SET, S1, S2, B, GS1S2, GS1, GS2, GB](
Ap[GS2, GS1S2, GS1, E, S1, S2],
Map[GB, GS1S2, E, B, func(S1) S2],
s,
fb,
)
}
func Let[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
SET ~func(B) func(S1) S2,
FCT ~func(S1) B,
E, S1, S2, B any](
s SET,
f FCT,
) func(GS1) GS2 {
return G.Let[SET, FCT, S1, S2, B, GS1, GS2](Map[GS1, GS2, E, S1, S2], s, f)
}

View File

@@ -1,21 +1,6 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:00.7765028 +0200 CEST m=+0.080050201
// 2023-08-17 22:59:12.0246635 +0200 CEST m=+0.118092001
package generic
import (

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:03.8471425 +0200 CEST m=+0.087124201
// 2023-08-17 22:59:14.0582736 +0200 CEST m=+0.248503201
package iooption
import (

View File

@@ -1,21 +1,6 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:03.8541587 +0200 CEST m=+0.094140401
// 2023-08-17 22:59:14.0642289 +0200 CEST m=+0.254458501
package generic
import (

View File

@@ -20,6 +20,7 @@ import (
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/utils"
IO "github.com/IBM/fp-go/iooption/generic"
M "github.com/IBM/fp-go/monoid"
N "github.com/IBM/fp-go/number"
O "github.com/IBM/fp-go/option"
T "github.com/IBM/fp-go/tuple"
@@ -219,3 +220,22 @@ func Ap[GUV ~func() O.Option[T.Tuple2[GUV, func(U) V]], GV ~func() O.Option[T.Tu
func MonadAp[GUV ~func() O.Option[T.Tuple2[GUV, func(U) V]], GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](fab GUV, ma GU) GV {
return Ap[GUV, GV, GU](ma)(fab)
}
func FilterChain[GVV ~func() O.Option[T.Tuple2[GVV, GV]], GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) O.Option[GV], U, V any](f FCT) func(ma GU) GV {
return F.Flow2(
FilterMap[GVV, GU](f),
Flatten[GVV],
)
}
func FoldMap[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) V, U, V any](m M.Monoid[V]) func(FCT) func(ma GU) V {
return func(f FCT) func(ma GU) V {
return Reduce[GU](func(cur V, a U) V {
return m.Concat(cur, f(a))
}, m.Empty())
}
}
func Fold[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m M.Monoid[U]) func(ma GU) U {
return Reduce[GU](m.Concat, m.Empty())
}

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 generic
import (
R "reflect"
F "github.com/IBM/fp-go/function"
LG "github.com/IBM/fp-go/io/generic"
L "github.com/IBM/fp-go/lazy"
N "github.com/IBM/fp-go/number"
I "github.com/IBM/fp-go/number/integer"
O "github.com/IBM/fp-go/option"
T "github.com/IBM/fp-go/tuple"
)
func FromReflect[GR ~func() O.Option[T.Tuple2[GR, R.Value]]](val R.Value) GR {
// recursive callback
var recurse func(idx int) GR
// limits the index
fromPred := O.FromPredicate(I.Between(0, val.Len()))
recurse = func(idx int) GR {
return F.Pipe3(
idx,
L.Of[int],
L.Map(fromPred),
LG.Map[L.Lazy[O.Option[int]], GR](O.Map(
F.Flow2(
T.Replicate2[int],
T.Map2(F.Flow2(N.Add(1), recurse), val.Index),
),
)),
)
}
// start the recursion
return recurse(0)
}

View File

@@ -18,6 +18,7 @@ package stateless
import (
G "github.com/IBM/fp-go/iterator/stateless/generic"
L "github.com/IBM/fp-go/lazy"
M "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
T "github.com/IBM/fp-go/tuple"
)
@@ -118,3 +119,18 @@ func Repeat[U any](n int, a U) Iterator[U] {
func Count(start int) Iterator[int] {
return G.Count[Iterator[int]](start)
}
// FilterChain filters and transforms the content of an iterator
func FilterChain[U, V any](f func(U) O.Option[Iterator[V]]) func(ma Iterator[U]) Iterator[V] {
return G.FilterChain[Iterator[Iterator[V]], Iterator[V], Iterator[U]](f)
}
// FoldMap maps and folds an iterator. Map the iterator passing each value to the iterating function. Then fold the results using the provided Monoid.
func FoldMap[U, V any](m M.Monoid[V]) func(func(U) V) func(ma Iterator[U]) V {
return G.FoldMap[Iterator[U], func(U) V, U, V](m)
}
// Fold folds the iterator using the provided Monoid.
func Fold[U any](m M.Monoid[U]) func(Iterator[U]) U {
return G.Fold[Iterator[U]](m)
}

View File

@@ -18,12 +18,14 @@ package stateless
import (
"fmt"
"math"
"strings"
"testing"
A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/utils"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
"github.com/stretchr/testify/assert"
)
@@ -102,3 +104,14 @@ func TestAp(t *testing.T) {
assert.Equal(t, A.From("a-1-c", "a-1-d", "a-2-c", "a-2-d", "b-1-c", "b-1-d", "b-2-c", "b-2-d"), it)
}
func ExampleFoldMap() {
src := From("a", "b", "c")
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
fmt.Println(fold(src))
// Output: ABC
}

View File

@@ -0,0 +1,27 @@
// 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 stateless
import (
R "reflect"
G "github.com/IBM/fp-go/iterator/stateless/generic"
)
// FromReflect creates an iterator that can iterate over types that define [R.Index] and [R.Len]
func FromReflect(val R.Value) Iterator[R.Value] {
return G.FromReflect[Iterator[R.Value]](val)
}

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 stateless
import (
"reflect"
"testing"
A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
"github.com/stretchr/testify/assert"
)
func TestReflect(t *testing.T) {
ar := A.From("a", "b", "c")
res := F.Pipe3(
reflect.ValueOf(ar),
FromReflect,
ToArray[reflect.Value],
A.Map(reflect.Value.String),
)
assert.Equal(t, ar, res)
}

View File

@@ -32,6 +32,6 @@ func ToTypeE[A any](src any) E.Either[error, A] {
func ToTypeO[A any](src any) O.Option[A] {
return F.Pipe1(
ToTypeE[A](src),
E.ToOption[error, A](),
E.ToOption[error, A],
)
}

38
lazy/example_lazy_test.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 lazy
import (
"fmt"
"strconv"
F "github.com/IBM/fp-go/function"
)
func ExampleLazy_creation() {
// lazy function of a constant value
val := Of(42)
// create another function to transform this
valS := F.Pipe1(
val,
Map(strconv.Itoa),
)
fmt.Println(valS())
// Output:
// 42
}

View File

@@ -72,18 +72,18 @@ func Chain[A, B any](f func(A) Lazy[B]) func(Lazy[A]) Lazy[B] {
}
func MonadAp[B, A any](mab Lazy[func(A) B], ma Lazy[A]) Lazy[B] {
return G.MonadAp[Lazy[A], Lazy[B]](mab, ma)
return G.MonadApSeq[Lazy[A], Lazy[B]](mab, ma)
}
func Ap[B, A any](ma Lazy[A]) func(Lazy[func(A) B]) Lazy[B] {
return G.Ap[Lazy[B], Lazy[func(A) B], Lazy[A]](ma)
return G.ApSeq[Lazy[B], Lazy[func(A) B], Lazy[A]](ma)
}
func Flatten[A any](mma Lazy[Lazy[A]]) Lazy[A] {
return G.Flatten(mma)
}
// Memoize computes the value of the provided IO monad lazily but exactly once
// Memoize computes the value of the provided [Lazy] monad lazily but exactly once
func Memoize[A any](ma Lazy[A]) Lazy[A] {
return G.Memoize(ma)
}

23
number/integer/string.go Normal file
View File

@@ -0,0 +1,23 @@
// 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 integer
import "strconv"
var (
// ToString converts an integer to a string
ToString = strconv.Itoa
)

View File

@@ -24,3 +24,9 @@ func MagmaSub[A Number]() M.Magma[A] {
return first - second
})
}
func MagmaDiv[A Number]() M.Magma[A] {
return M.MakeMagma(func(first A, second A) A {
return first / second
})
}

View File

@@ -19,6 +19,7 @@ import (
M "github.com/IBM/fp-go/monoid"
)
// MonoidSum is the [Monoid] that adds elements with a zero empty element
func MonoidSum[A Number]() M.Monoid[A] {
s := SemigroupSum[A]()
return M.MakeMonoid(
@@ -26,3 +27,12 @@ func MonoidSum[A Number]() M.Monoid[A] {
0,
)
}
// MonoidProduct is the [Monoid] that multiplies elements with a one empty element
func MonoidProduct[A Number]() M.Monoid[A] {
s := SemigroupProduct[A]()
return M.MakeMonoid(
s.Concat,
1,
)
}

View File

@@ -24,3 +24,9 @@ func SemigroupSum[A Number]() S.Semigroup[A] {
return first + second
})
}
func SemigroupProduct[A Number]() S.Semigroup[A] {
return S.MakeSemigroup(func(first A, second A) A {
return first * second
})
}

View File

@@ -24,12 +24,33 @@ type Number interface {
}
// Add is a curried function used to add two numbers
func Add[T Number](left T) func(T) T {
return func(right T) T {
func Add[T Number](right T) func(T) T {
return func(left T) T {
return left + right
}
}
// Sub is a curried function used to subtract two numbers
func Sub[T Number](right T) func(T) T {
return func(left T) T {
return left - right
}
}
// Mul is a curried function used to multiply two numbers
func Mul[T Number](right T) func(T) T {
return func(left T) T {
return left * right
}
}
// Div is a curried function used to divide two numbers
func Div[T Number](right T) func(T) T {
return func(left T) T {
return left / right
}
}
// Inc is a function that increments a number
func Inc[T Number](value T) T {
return value + 1

View File

@@ -44,3 +44,15 @@ func SequenceArrayG[GA ~[]A, GOA ~[]Option[A], A any](ma GOA) Option[GA] {
func SequenceArray[A any](ma []Option[A]) Option[[]A] {
return SequenceArrayG[[]A](ma)
}
// CompactArrayG discards the none values and keeps the some values
func CompactArrayG[A1 ~[]Option[A], A2 ~[]A, A any](fa A1) A2 {
return RA.Reduce(fa, func(out A2, value Option[A]) A2 {
return MonadFold(value, F.Constant(out), F.Bind1st(RA.Append[A2, A], out))
}, make(A2, len(fa)))
}
// CompactArray discards the none values and keeps the some values
func CompactArray[A any](fa []Option[A]) []A {
return CompactArrayG[[]Option[A], []A](fa)
}

View File

@@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package option defines the [Option] datastructure and its monadic operations
package option
//go:generate go run .. option --count 10 --filename gen.go

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 option
import "fmt"
func ExampleOption_creation() {
// Build an Option
none1 := None[int]()
some1 := Some("value")
// Build from a value
fromNillable := FromNillable[string]
nonFromNil := fromNillable(nil) // None[*string]
value := "value"
someFromPointer := fromNillable(&value) // Some[*string](xxx)
// some predicate
isEven := func(num int) bool {
return num%2 == 0
}
fromEven := FromPredicate(isEven)
noneFromPred := fromEven(3) // None[int]
someFromPred := fromEven(4) // Some[int](4)
fmt.Println(none1)
fmt.Println(some1)
fmt.Println(nonFromNil)
fmt.Println(IsSome(someFromPointer))
fmt.Println(noneFromPred)
fmt.Println(someFromPred)
// Output:
// None[int]
// Some[string](value)
// None[*string]
// true
// None[int]
// Some[int](4)
}

View File

@@ -0,0 +1,73 @@
// 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 option
import (
"fmt"
F "github.com/IBM/fp-go/function"
N "github.com/IBM/fp-go/number"
)
func ExampleOption_extraction() {
noneValue := None[int]()
someValue := Of(42)
// Convert Option[T] to T
fromNone, okFromNone := Unwrap(noneValue) // 0, false
fromSome, okFromSome := Unwrap(someValue) // 42, true
// Convert Option[T] with a default value
noneWithDefault := GetOrElse(F.Constant(0))(noneValue) // 0
someWithDefault := GetOrElse(F.Constant(0))(someValue) // 42
// Apply a different function on None/Some(...)
doubleOrZero := Fold(
F.Constant(0), // none case
N.Mul(2), // some case
) // func(ma Option[int]) int
doubleFromNone := doubleOrZero(noneValue) // 0
doubleFromSome := doubleOrZero(someValue) // 84
// Pro-tip: Fold is short for the following:
doubleOfZeroBis := F.Flow2(
Map(N.Mul(2)), // some case
GetOrElse(F.Constant(0)), // none case
)
doubleFromNoneBis := doubleOfZeroBis(noneValue) // 0
doubleFromSomeBis := doubleOfZeroBis(someValue) // 84
fmt.Printf("%d, %t\n", fromNone, okFromNone)
fmt.Printf("%d, %t\n", fromSome, okFromSome)
fmt.Println(noneWithDefault)
fmt.Println(someWithDefault)
fmt.Println(doubleFromNone)
fmt.Println(doubleFromSome)
fmt.Println(doubleFromNoneBis)
fmt.Println(doubleFromSomeBis)
// Output:
// 0, false
// 42, true
// 0
// 42
// 0
// 84
// 0
// 84
}

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:06.6236747 +0200 CEST m=+0.039283301
// 2023-08-17 22:59:16.3450991 +0200 CEST m=+0.104894201
package option
import (

38
option/ord.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 option
import (
C "github.com/IBM/fp-go/constraints"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/ord"
)
// Constructs an order for [Option]
func Ord[A any](a ord.Ord[A]) ord.Ord[Option[A]] {
// some convenient shortcuts
fld := Fold(
F.Constant(Fold(F.Constant(0), F.Constant1[A](-1))),
F.Flow2(F.Curry2(a.Compare), F.Bind1st(Fold[A, int], F.Constant(1))),
)
// convert to an ordering predicate
return ord.MakeOrd(F.Uncurry2(fld), Eq(ord.ToEq(a)).Equals)
}
// FromStrictCompare constructs an [Ord] from the canonical comparison function
func FromStrictCompare[A C.Ordered]() ord.Ord[Option[A]] {
return Ord(ord.FromStrictCompare[A]())
}

46
option/ord_test.go Normal file
View File

@@ -0,0 +1,46 @@
// 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 option
import (
"testing"
S "github.com/IBM/fp-go/string"
"github.com/stretchr/testify/assert"
)
// it('getOrd', () => {
// const OS = _.getOrd(S.Ord)
// U.deepStrictEqual(OS.compare(_.none, _.none), 0)
// U.deepStrictEqual(OS.compare(_.some('a'), _.none), 1)
// U.deepStrictEqual(OS.compare(_.none, _.some('a')), -1)
// U.deepStrictEqual(OS.compare(_.some('a'), _.some('a')), 0)
// U.deepStrictEqual(OS.compare(_.some('a'), _.some('b')), -1)
// U.deepStrictEqual(OS.compare(_.some('b'), _.some('a')), 1)
// })
func TestOrd(t *testing.T) {
os := Ord(S.Ord)
assert.Equal(t, 0, os.Compare(None[string](), None[string]()))
assert.Equal(t, 1, os.Compare(Some("a"), None[string]()))
assert.Equal(t, -1, os.Compare(None[string](), Some("a")))
assert.Equal(t, 0, os.Compare(Some("a"), Some("a")))
assert.Equal(t, -1, os.Compare(Some("a"), Some("b")))
assert.Equal(t, 1, os.Compare(Some("b"), Some("a")))
}

View File

@@ -44,3 +44,21 @@ func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Option[A], K comparable, A any](ma
func SequenceRecord[K comparable, A any](ma map[K]Option[A]) Option[map[K]A] {
return SequenceRecordG[map[K]A](ma)
}
func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M {
r[k] = v
return r
}
// CompactRecordG discards the noe values and keeps the some values
func CompactRecordG[M1 ~map[K]Option[A], M2 ~map[K]A, K comparable, A any](m M1) M2 {
bnd := F.Bind12of3(upsertAtReadWrite[M2])
return RR.ReduceWithIndex(m, func(key K, m M2, value Option[A]) M2 {
return MonadFold(value, F.Constant(m), bnd(m, key))
}, make(M2))
}
// CompactRecord discards the noe values and keeps the some values
func CompactRecord[K comparable, A any](m map[K]Option[A]) map[K]A {
return CompactRecordG[map[K]Option[A], map[K]A](m)
}

View File

@@ -30,3 +30,18 @@ func TestSequenceRecord(t *testing.T) {
"b": Of("B"),
}))
}
func TestCompactRecord(t *testing.T) {
// make the map
m := make(map[string]Option[int])
m["foo"] = None[int]()
m["bar"] = Some(1)
// compact it
m1 := CompactRecord(m)
// check expected
exp := map[string]int{
"bar": 1,
}
assert.Equal(t, exp, m1)
}

View File

@@ -19,13 +19,22 @@ import (
F "github.com/IBM/fp-go/function"
)
// HKTA = HKT<A>
// HKTOA = HKT<Option<A>>
//
// Sequence converts an option of some higher kinded type into the higher kinded type of an option
// Sequence converts an [Option] of some higher kinded type into the higher kinded type of an [Option]
func Sequence[A, HKTA, HKTOA any](
_of func(Option[A]) HKTOA,
_map func(HKTA, func(A) Option[A]) HKTOA,
mof func(Option[A]) HKTOA,
mmap func(func(A) Option[A]) func(HKTA) HKTOA,
) func(Option[HKTA]) HKTOA {
return Fold(F.Nullary2(None[A], _of), F.Bind2nd(_map, Some[A]))
return Fold(F.Nullary2(None[A], mof), mmap(Some[A]))
}
// Traverse converts an [Option] of some higher kinded type into the higher kinded type of an [Option]
func Traverse[A, B, HKTB, HKTOB any](
mof func(Option[B]) HKTOB,
mmap func(func(B) Option[B]) func(HKTB) HKTOB,
) func(func(A) HKTB) func(Option[A]) HKTOB {
onNone := F.Nullary2(None[B], mof)
onSome := mmap(Some[B])
return func(f func(A) HKTB) func(Option[A]) HKTOB {
return Fold(onNone, F.Flow2(f, onSome))
}
}

View File

@@ -23,7 +23,7 @@ import (
O "github.com/IBM/fp-go/option"
)
// AssertLaws asserts the apply monad laws for the `Either` monad
// AssertLaws asserts the apply monad laws for the [Option] monad
func AssertLaws[A, B, C any](t *testing.T,
eqa EQ.Eq[A],
eqb EQ.Eq[B],

View File

@@ -40,6 +40,11 @@ func (self ord[T]) Compare(x, y T) int {
return self.c(x, y)
}
// ToEq converts an [Ord] to [E.Eq]
func ToEq[T any](o Ord[T]) E.Eq[T] {
return o
}
// MakeOrd creates an instance of an Ord
func MakeOrd[T any](c func(x, y T) int, e func(x, y T) bool) Ord[T] {
return ord[T]{c: c, e: e}
@@ -126,9 +131,7 @@ func FromStrictCompare[A C.Ordered]() Ord[A] {
return MakeOrd(strictCompare[A], strictEq[A])
}
/**
* Test whether one value is _strictly less than_ another
*/
// Lt tests whether one value is strictly less than another
func Lt[A any](O Ord[A]) func(A) func(A) bool {
return func(second A) func(A) bool {
return func(first A) bool {
@@ -137,9 +140,7 @@ func Lt[A any](O Ord[A]) func(A) func(A) bool {
}
}
/**
* Test whether one value is less or equal than_ another
*/
// Leq Tests whether one value is less or equal than another
func Leq[A any](O Ord[A]) func(A) func(A) bool {
return func(second A) func(A) bool {
return func(first A) bool {
@@ -149,7 +150,7 @@ func Leq[A any](O Ord[A]) func(A) func(A) bool {
}
/**
* Test whether one value is _strictly greater than_ another
* Test whether one value is strictly greater than another
*/
func Gt[A any](O Ord[A]) func(A) func(A) bool {
return func(second A) func(A) bool {
@@ -159,9 +160,7 @@ func Gt[A any](O Ord[A]) func(A) func(A) bool {
}
}
/**
* Test whether one value is greater or equal than_ another
*/
// Geq tests whether one value is greater or equal than another
func Geq[A any](O Ord[A]) func(A) func(A) bool {
return func(second A) func(A) bool {
return func(first A) bool {

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:14.4173227 +0200 CEST m=+0.013163201
// 2023-08-17 22:59:18.4448291 +0200 CEST m=+0.161369001
package reader
import (

View File

@@ -1,21 +1,6 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:14.4173227 +0200 CEST m=+0.013163201
// 2023-08-17 22:59:18.5047315 +0200 CEST m=+0.221271401
package generic
// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [GRA]

View File

@@ -32,7 +32,7 @@ func FromEither[E, L, A any](e ET.Either[L, A]) ReaderEither[E, L, A] {
return G.FromEither[ReaderEither[E, L, A]](e)
}
func RightReader[E, L, A any](r R.Reader[E, A]) ReaderEither[E, L, A] {
func RightReader[L, E, A any](r R.Reader[E, A]) ReaderEither[E, L, A] {
return G.RightReader[R.Reader[E, A], ReaderEither[E, L, A]](r)
}
@@ -76,7 +76,7 @@ func MonadAp[E, L, A, B any](fab ReaderEither[E, L, func(A) B], fa ReaderEither[
return G.MonadAp[ReaderEither[E, L, A], ReaderEither[E, L, B], ReaderEither[E, L, func(A) B]](fab, fa)
}
func Ap[E, L, A, B any](fa ReaderEither[E, L, A]) func(ReaderEither[E, L, func(A) B]) ReaderEither[E, L, B] {
func Ap[B, E, L, A any](fa ReaderEither[E, L, A]) func(ReaderEither[E, L, func(A) B]) ReaderEither[E, L, B] {
return G.Ap[ReaderEither[E, L, A], ReaderEither[E, L, B], ReaderEither[E, L, func(A) B]](fa)
}
@@ -96,7 +96,7 @@ func OrElse[E, L1, A, L2 any](onLeft func(L1) ReaderEither[E, L2, A]) func(Reade
return G.OrElse[ReaderEither[E, L1, A]](onLeft)
}
func OrLeft[L1, E, L2, A any](onLeft func(L1) R.Reader[E, L2]) func(ReaderEither[E, L1, A]) ReaderEither[E, L2, A] {
func OrLeft[A, L1, E, L2 any](onLeft func(L1) R.Reader[E, L2]) func(ReaderEither[E, L1, A]) ReaderEither[E, L2, A] {
return G.OrLeft[ReaderEither[E, L1, A], ReaderEither[E, L2, A]](onLeft)
}
@@ -104,19 +104,19 @@ func Ask[E, L any]() ReaderEither[E, L, E] {
return G.Ask[ReaderEither[E, L, E]]()
}
func Asks[E, L, A any](r R.Reader[E, A]) ReaderEither[E, L, A] {
func Asks[L, E, A any](r R.Reader[E, A]) ReaderEither[E, L, A] {
return G.Asks[R.Reader[E, A], ReaderEither[E, L, A]](r)
}
func MonadChainEitherK[E, L, A, B any](ma ReaderEither[E, L, A], f func(A) ET.Either[L, B]) ReaderEither[E, L, B] {
func MonadChainEitherK[A, B, L, E any](ma ReaderEither[E, L, A], f func(A) ET.Either[L, B]) ReaderEither[E, L, B] {
return G.MonadChainEitherK[ReaderEither[E, L, A], ReaderEither[E, L, B]](ma, f)
}
func ChainEitherK[E, L, A, B any](f func(A) ET.Either[L, B]) func(ma ReaderEither[E, L, A]) ReaderEither[E, L, B] {
func ChainEitherK[A, B, L, E any](f func(A) ET.Either[L, B]) func(ma ReaderEither[E, L, A]) ReaderEither[E, L, B] {
return G.ChainEitherK[ReaderEither[E, L, A], ReaderEither[E, L, B]](f)
}
func ChainOptionK[E, L, A, B any](onNone func() L) func(func(A) O.Option[B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B] {
func ChainOptionK[E, A, B, L any](onNone func() L) func(func(A) O.Option[B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B] {
return G.ChainOptionK[ReaderEither[E, L, A], ReaderEither[E, L, B]](onNone)
}
@@ -135,11 +135,11 @@ func BiMap[E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderEither[E,
// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s
// `contramap`).
func Local[R2, R1, E, A any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A] {
func Local[E, A, R2, R1 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A] {
return G.Local[ReaderEither[R1, E, A], ReaderEither[R2, E, A]](f)
}
// Read applies a context to a reader to obtain its value
func Read[E, E1, A any](e E) func(ReaderEither[E, E1, A]) ET.Either[E1, A] {
func Read[E1, A, E any](e E) func(ReaderEither[E, E1, A]) ET.Either[E1, A] {
return G.Read[ReaderEither[E, E1, A]](e)
}

View File

@@ -42,7 +42,7 @@ func TestMap(t *testing.T) {
func TestAp(t *testing.T) {
g := F.Pipe1(
Of[MyContext, error](utils.Double),
Ap[MyContext, error, int, int](Of[MyContext, error](1)),
Ap[int](Of[MyContext, error](1)),
)
assert.Equal(t, ET.Of[error](2), g(defaultContext))

View File

@@ -1,21 +1,7 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:16.5272334 +0200 CEST m=+0.043108701
// 2023-08-17 22:59:21.3704716 +0200 CEST m=+0.110126101
package readerioeither
import (

View File

@@ -1,21 +1,6 @@
// 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.
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-28 22:49:16.5292332 +0200 CEST m=+0.045108501
// 2023-08-17 22:59:21.3811525 +0200 CEST m=+0.120807001
package generic
import (

View File

@@ -93,7 +93,7 @@ func MonadChainReaderK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) RD.
return G.MonadChainReaderK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f)
}
func ChainReaderK[R, E, A, B any](f func(A) RD.Reader[R, B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
func ChainReaderK[E, R, A, B any](f func(A) RD.Reader[R, B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
return G.ChainReaderK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f)
}
@@ -121,7 +121,7 @@ func ChainFirstIOK[R, E, A, B any](f func(A) io.IO[B]) func(ma ReaderIOEither[R,
return G.ChainFirstIOK[ReaderIOEither[R, E, A]](f)
}
func ChainOptionK[R, E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
func ChainOptionK[R, A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
return G.ChainOptionK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](onNone)
}
@@ -149,7 +149,7 @@ func Left[R, A, E any](e E) ReaderIOEither[R, E, A] {
return G.Left[ReaderIOEither[R, E, A]](e)
}
func ThrowError[R, E, A any](e E) ReaderIOEither[R, E, A] {
func ThrowError[R, A, E any](e E) ReaderIOEither[R, E, A] {
return G.ThrowError[ReaderIOEither[R, E, A]](e)
}
@@ -166,7 +166,7 @@ func FromEither[R, E, A any](t ET.Either[E, A]) ReaderIOEither[R, E, A] {
return G.FromEither[ReaderIOEither[R, E, A]](t)
}
func RightReader[R, E, A any](ma RD.Reader[R, A]) ReaderIOEither[R, E, A] {
func RightReader[E, R, A any](ma RD.Reader[R, A]) ReaderIOEither[R, E, A] {
return G.RightReader[RD.Reader[R, A], ReaderIOEither[R, E, A]](ma)
}
@@ -174,7 +174,7 @@ func LeftReader[A, R, E any](ma RD.Reader[R, E]) ReaderIOEither[R, E, A] {
return G.LeftReader[RD.Reader[R, E], ReaderIOEither[R, E, A]](ma)
}
func FromReader[R, E, A any](ma RD.Reader[R, A]) ReaderIOEither[R, E, A] {
func FromReader[E, R, A any](ma RD.Reader[R, A]) ReaderIOEither[R, E, A] {
return G.FromReader[RD.Reader[R, A], ReaderIOEither[R, E, A]](ma)
}
@@ -202,11 +202,11 @@ func Ask[R, E any]() ReaderIOEither[R, E, R] {
return G.Ask[ReaderIOEither[R, E, R]]()
}
func Asks[R, E, A any](r RD.Reader[R, A]) ReaderIOEither[R, E, A] {
func Asks[E, R, A any](r RD.Reader[R, A]) ReaderIOEither[R, E, A] {
return G.Asks[RD.Reader[R, A], ReaderIOEither[R, E, A]](r)
}
func FromOption[R, E, A any](onNone func() E) func(O.Option[A]) ReaderIOEither[R, E, A] {
func FromOption[R, A, E any](onNone func() E) func(O.Option[A]) ReaderIOEither[R, E, A] {
return G.FromOption[ReaderIOEither[R, E, A]](onNone)
}
@@ -226,7 +226,7 @@ func OrElse[R, E1, A, E2 any](onLeft func(E1) ReaderIOEither[R, E2, A]) func(Rea
return G.OrElse[ReaderIOEither[R, E1, A]](onLeft)
}
func OrLeft[E1, R, E2, A any](onLeft func(E1) RIO.ReaderIO[R, E2]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
func OrLeft[A, E1, R, E2 any](onLeft func(E1) RIO.ReaderIO[R, E2]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
return G.OrLeft[ReaderIOEither[R, E1, A], RIO.ReaderIO[R, E2], ReaderIOEither[R, E2, A]](onLeft)
}

View File

@@ -39,7 +39,7 @@ func TestMap(t *testing.T) {
}
func TestOrLeft(t *testing.T) {
f := OrLeft[string, context.Context, string, int](func(s string) RIO.ReaderIO[context.Context, string] {
f := OrLeft[int](func(s string) RIO.ReaderIO[context.Context, string] {
return RIO.Of[context.Context](s + "!")
})
@@ -70,7 +70,7 @@ func TestChainReaderK(t *testing.T) {
g := F.Pipe1(
Of[context.Context, error](1),
ChainReaderK[context.Context, error](func(v int) R.Reader[context.Context, string] {
ChainReaderK[error](func(v int) R.Reader[context.Context, string] {
return R.Of[context.Context](fmt.Sprintf("%d", v))
}),
)

17
record/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 record contains monadic operations for maps as well as a rich set of utility functions
package record

View File

@@ -16,6 +16,7 @@
package generic
import (
F "github.com/IBM/fp-go/function"
M "github.com/IBM/fp-go/monoid"
S "github.com/IBM/fp-go/semigroup"
)
@@ -26,3 +27,17 @@ func UnionMonoid[N ~map[K]V, K comparable, V any](s S.Semigroup[V]) M.Monoid[N]
Empty[N](),
)
}
func UnionLastMonoid[N ~map[K]V, K comparable, V any]() M.Monoid[N] {
return M.MakeMonoid(
unionLast[N],
Empty[N](),
)
}
func UnionFirstMonoid[N ~map[K]V, K comparable, V any]() M.Monoid[N] {
return M.MakeMonoid(
F.Swap(unionLast[N]),
Empty[N](),
)
}

View File

@@ -16,10 +16,14 @@
package generic
import (
"sort"
F "github.com/IBM/fp-go/function"
G "github.com/IBM/fp-go/internal/record"
Mg "github.com/IBM/fp-go/magma"
Mo "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/ord"
T "github.com/IBM/fp-go/tuple"
)
@@ -39,6 +43,46 @@ func Values[M ~map[K]V, GV ~[]V, K comparable, V any](r M) GV {
return collect[M, GV](r, F.Second[K, V])
}
func KeysOrd[M ~map[K]V, GK ~[]K, K comparable, V any](o ord.Ord[K]) func(r M) GK {
return func(r M) GK {
return collectOrd[M, GK](o, r, F.First[K, V])
}
}
func ValuesOrd[M ~map[K]V, GV ~[]V, K comparable, V any](o ord.Ord[K]) func(r M) GV {
return func(r M) GV {
return collectOrd[M, GV](o, r, F.Second[K, V])
}
}
func collectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K], r M, f func(K, V) R) GR {
// create the entries
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
// collect this array
ft := T.Tupled2(f)
count := len(entries)
result := make(GR, count)
for i := count - 1; i >= 0; i-- {
result[i] = ft(entries[i])
}
// done
return result
}
func reduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K], r M, f func(K, R, V) R, initial R) R {
// create the entries
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
// collect this array
current := initial
count := len(entries)
for i := 0; i < count; i++ {
t := entries[i]
current = f(T.First(t), current, T.Second(t))
}
// done
return current
}
func collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](r M, f func(K, V) R) GR {
count := len(r)
result := make(GR, count)
@@ -82,6 +126,34 @@ func MonadMap[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(V) R)
return MonadMapWithIndex[M, N](r, F.Ignore1of2[K](f))
}
func MonadChainWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N], r M, f func(K, V1) N) N {
return G.ReduceWithIndex(r, func(k K, dst N, b V1) N {
return m.Concat(dst, f(k, b))
}, m.Empty())
}
func MonadChain[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N], r M, f func(V1) N) N {
return G.Reduce(r, func(dst N, b V1) N {
return m.Concat(dst, f(b))
}, m.Empty())
}
func ChainWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(K, V1) N) func(M) N {
return func(f func(K, V1) N) func(M) N {
return func(ma M) N {
return MonadChainWithIndex(m, ma, f)
}
}
}
func Chain[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(V1) N) func(M) N {
return func(f func(V1) N) func(M) N {
return func(ma M) N {
return MonadChain(m, ma, f)
}
}
}
func MonadMapWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(K, V) R) N {
return G.ReduceWithIndex(r, func(k K, dst N, v V) N {
return upsertAtReadWrite(dst, k, f(k, v))
@@ -114,15 +186,21 @@ func MapRefWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(K, *
return F.Bind2nd(MonadMapRefWithIndex[M, N, K, V, R], f)
}
func lookup[M ~map[K]V, K comparable, V any](r M, k K) O.Option[V] {
if val, ok := r[k]; ok {
func MonadLookup[M ~map[K]V, K comparable, V any](m M, k K) O.Option[V] {
if val, ok := m[k]; ok {
return O.Some(val)
}
return O.None[V]()
}
func Lookup[M ~map[K]V, K comparable, V any](k K) func(M) O.Option[V] {
return F.Bind2nd(lookup[M, K, V], k)
n := O.None[V]()
return func(m M) O.Option[V] {
if val, ok := m[k]; ok {
return O.Some(val)
}
return n
}
}
func Has[M ~map[K]V, K comparable, V any](k K, r M) bool {
@@ -161,6 +239,31 @@ func union[M ~map[K]V, K comparable, V any](m Mg.Magma[V], left M, right M) M {
return result
}
func unionLast[M ~map[K]V, K comparable, V any](left M, right M) M {
lenLeft := len(left)
if lenLeft == 0 {
return right
}
lenRight := len(right)
if lenRight == 0 {
return left
}
result := make(M, lenLeft+lenRight)
for k, v := range left {
result[k] = v
}
for k, v := range right {
result[k] = v
}
return result
}
func Union[M ~map[K]V, K comparable, V any](m Mg.Magma[V]) func(M) func(M) M {
return func(right M) func(M) M {
return func(left M) M {
@@ -169,6 +272,22 @@ func Union[M ~map[K]V, K comparable, V any](m Mg.Magma[V]) func(M) func(M) M {
}
}
func UnionLast[M ~map[K]V, K comparable, V any](right M) func(M) M {
return func(left M) M {
return unionLast(left, right)
}
}
func Merge[M ~map[K]V, K comparable, V any](right M) func(M) M {
return UnionLast(right)
}
func UnionFirst[M ~map[K]V, K comparable, V any](right M) func(M) M {
return func(left M) M {
return unionLast(right, left)
}
}
func Empty[M ~map[K]V, K comparable, V any]() M {
return make(M)
}
@@ -181,6 +300,27 @@ func ToArray[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return collect[M, GT](r, T.MakeTuple2[K, V])
}
func toEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K], r M) GT {
// total number of elements
count := len(r)
// produce an array that we can sort by key
entries := make(GT, count)
idx := 0
for k, v := range r {
entries[idx] = T.MakeTuple2(k, v)
idx++
}
sort.Slice(entries, func(i, j int) bool {
return o.Compare(T.First(entries[i]), T.First(entries[j])) < 0
})
// final entries
return entries
}
func ToEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K]) func(r M) GT {
return F.Bind1st(toEntriesOrd[M, GT, K, V], o)
}
func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return ToArray[M, GT](r)
}
@@ -269,6 +409,33 @@ func FilterMap[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](f func(V1) O.
return F.Bind2nd(filterMapWithIndex[M, N, K, V1, V2], F.Ignore1of2[K](f))
}
// Flatten converts a nested map into a regular map
func Flatten[M ~map[K]N, N ~map[K]V, K comparable, V any](m Mo.Monoid[N]) func(M) N {
return Chain[M, N](m)(F.Identity[N])
}
// FilterChainWithIndex creates a new map with only the elements for which the transformation function creates a Some
func FilterChainWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(K, V1) O.Option[N]) func(M) N {
flatten := Flatten[map[K]N, N](m)
return func(f func(K, V1) O.Option[N]) func(M) N {
return F.Flow2(
FilterMapWithIndex[M, map[K]N](f),
flatten,
)
}
}
// FilterChain creates a new map with only the elements for which the transformation function creates a Some
func FilterChain[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(V1) O.Option[N]) func(M) N {
flatten := Flatten[map[K]N, N](m)
return func(f func(V1) O.Option[N]) func(M) N {
return F.Flow2(
FilterMap[M, map[K]N](f),
flatten,
)
}
}
// IsNil checks if the map is set to nil
func IsNil[M ~map[K]V, K comparable, V any](m M) bool {
return m == nil
@@ -283,3 +450,67 @@ func IsNonNil[M ~map[K]V, K comparable, V any](m M) bool {
func ConstNil[M ~map[K]V, K comparable, V any]() M {
return (M)(nil)
}
func FoldMap[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(AS) B {
return func(f func(A) B) func(AS) B {
return Reduce[AS](func(cur B, a A) B {
return m.Concat(cur, f(a))
}, m.Empty())
}
}
func Fold[AS ~map[K]A, K comparable, A any](m Mo.Monoid[A]) func(AS) A {
return Reduce[AS](m.Concat, m.Empty())
}
func FoldMapWithIndex[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
return func(f func(K, A) B) func(AS) B {
return ReduceWithIndex[AS](func(k K, cur B, a A) B {
return m.Concat(cur, f(k, a))
}, m.Empty())
}
}
func ReduceOrdWithIndex[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(K, R, V) R, R) func(M) R {
return func(f func(K, R, V) R, initial R) func(M) R {
return func(m M) R {
return reduceOrd(o, m, f, initial)
}
}
}
func ReduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(R, V) R, R) func(M) R {
ro := ReduceOrdWithIndex[M, K, V, R](o)
return func(f func(R, V) R, initial R) func(M) R {
return ro(F.Ignore1of3[K](f), initial)
}
}
func FoldMapOrd[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(AS) B {
red := ReduceOrd[AS, K, A, B](o)
return func(m Mo.Monoid[B]) func(func(A) B) func(AS) B {
return func(f func(A) B) func(AS) B {
return red(func(cur B, a A) B {
return m.Concat(cur, f(a))
}, m.Empty())
}
}
}
func FoldOrd[AS ~map[K]A, K comparable, A any](o ord.Ord[K]) func(m Mo.Monoid[A]) func(AS) A {
red := ReduceOrd[AS, K, A, A](o)
return func(m Mo.Monoid[A]) func(AS) A {
return red(m.Concat, m.Empty())
}
}
func FoldMapOrdWithIndex[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
red := ReduceOrdWithIndex[AS, K, A, B](o)
return func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
return func(f func(K, A) B) func(AS) B {
return red(func(k K, cur B, a A) B {
return m.Concat(cur, f(k, a))
}, m.Empty())
}
}
}

Some files were not shown because too many files have changed in this diff Show More