mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-28 19:49:07 +02:00
Compare commits
34 Commits
v1.0.18
...
cleue-add-
Author | SHA1 | Date | |
---|---|---|---|
|
ff1b6faf84 | ||
|
38120764e7 | ||
|
a83c2aec49 | ||
|
03debd37c8 | ||
|
4f04344cda | ||
|
cf1c886f48 | ||
|
3e0eb99b88 | ||
|
cb15a3d9fc | ||
|
16535605f5 | ||
|
53f4e5ebd7 | ||
|
fb91fd5dc8 | ||
|
3ccafb5302 | ||
|
52b71ef4f3 | ||
|
5d77d5bb3d | ||
|
8ba8f852fa | ||
|
29d9882d2a | ||
|
f80ca31e14 | ||
|
8692078972 | ||
|
12a4f6801c | ||
|
8650a8a600 | ||
|
fb3b1f115c | ||
|
ce66cf2295 | ||
|
80e579dd0b | ||
|
ddafd1ee57 | ||
|
b5f077da71 | ||
|
1a0c40b419 | ||
|
d5d89b1853 | ||
|
0f061a5099 | ||
|
45e05f25ff | ||
|
a390d53451 | ||
|
1346b9378a | ||
|
befd4f471e | ||
|
db8d3da87a | ||
|
ee4e936183 |
@@ -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]
|
||||
|
@@ -304,3 +304,7 @@ func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||
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)
|
||||
}
|
||||
|
59
array/examples_basic_test.go
Normal file
59
array/examples_basic_test.go
Normal 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]
|
||||
}
|
92
array/examples_sort_test.go
Normal file
92
array/examples_sort_test.go
Normal 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}}]
|
||||
|
||||
}
|
@@ -28,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
|
||||
@@ -105,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)
|
||||
}
|
||||
@@ -175,6 +187,14 @@ func IsEmpty[AS ~[]A, A any](as AS) bool {
|
||||
return array.IsEmpty(as)
|
||||
}
|
||||
|
||||
func IsNil[GA ~[]A, A any](as GA) bool {
|
||||
return array.IsNil(as)
|
||||
}
|
||||
|
||||
func IsNonNil[GA ~[]A, A any](as GA) bool {
|
||||
return array.IsNonNil(as)
|
||||
}
|
||||
|
||||
func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B {
|
||||
return func(as AS) B {
|
||||
if IsEmpty(as) {
|
||||
@@ -226,3 +246,7 @@ func Fold[AS ~[]A, A any](m M.Monoid[A]) func(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)
|
||||
}
|
||||
|
@@ -45,3 +45,12 @@ func SortByKey[GA ~[]T, K, T any](ord O.Ord[K], f func(T) K) func(ma GA) GA {
|
||||
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],
|
||||
)
|
||||
}
|
||||
|
@@ -29,3 +29,8 @@ func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
||||
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)
|
||||
}
|
||||
|
50
cli/tuple.go
50
cli/tuple.go
@@ -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++ {
|
||||
@@ -398,6 +442,10 @@ import (
|
||||
generateToArray(f, i)
|
||||
// generate fromArray
|
||||
generateFromArray(f, i)
|
||||
// generate push
|
||||
if i < count {
|
||||
generatePush(f, i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -2,7 +2,7 @@ package readerioeither
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:37:34.0836986 +0200 CEST m=+0.009764601
|
||||
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@@ -2,7 +2,7 @@ package generic
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:37:34.0838811 +0200 CEST m=+0.009947101
|
||||
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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] {
|
||||
@@ -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,
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:37:35.9608486 +0200 CEST m=+0.009664501
|
||||
// 2023-08-17 22:58:57.9925506 +0200 CEST m=+0.033401101
|
||||
|
||||
package either
|
||||
|
||||
|
@@ -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
37
either/record_test.go
Normal 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)
|
||||
}
|
@@ -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]))
|
||||
}
|
||||
|
@@ -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))))
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:37:50.171038 +0200 CEST m=+0.031513801
|
||||
// 2023-08-17 22:59:01.9404821 +0200 CEST m=+0.161366801
|
||||
|
||||
package function
|
||||
|
||||
|
30
function/cache.go
Normal file
30
function/cache.go
Normal 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
50
function/cache_test.go
Normal 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)
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:37:38.4916948 +0200 CEST m=+0.060448601
|
||||
// 2023-08-17 22:58:59.8663985 +0200 CEST m=+0.103744101
|
||||
|
||||
package function
|
||||
|
||||
|
65
function/generic/cache.go
Normal file
65
function/generic/cache.go
Normal 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)
|
||||
})
|
||||
})()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:00.2678592 +0200 CEST m=+0.008890401
|
||||
// 2023-08-17 22:59:06.2987136 +0200 CEST m=+0.049156901
|
||||
|
||||
package identity
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:01.6948567 +0200 CEST m=+0.013530301
|
||||
// 2023-08-17 22:59:08.1313362 +0200 CEST m=+0.111171801
|
||||
|
||||
package apply
|
||||
|
||||
|
@@ -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
75
internal/bindt/bind.go
Normal 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
34
internal/lazy/memoize.go
Normal 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
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:07.3305497 +0200 CEST m=+0.014821301
|
||||
// 2023-08-17 22:59:10.1040409 +0200 CEST m=+0.089470201
|
||||
|
||||
package io
|
||||
|
||||
|
@@ -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(
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:07.3305497 +0200 CEST m=+0.014821301
|
||||
// 2023-08-17 22:59:10.1132645 +0200 CEST m=+0.098693801
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
@@ -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
44
ioeither/bind.go
Normal 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)
|
||||
}
|
57
ioeither/examples_create_test.go
Normal file
57
ioeither/examples_create_test.go
Normal 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)
|
||||
|
||||
}
|
57
ioeither/examples_do_test.go
Normal file
57
ioeither/examples_do_test.go
Normal 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)
|
||||
}
|
45
ioeither/examples_extract_test.go
Normal file
45
ioeither/examples_extract_test.go
Normal 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
|
||||
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:09.4187123 +0200 CEST m=+0.099620101
|
||||
// 2023-08-17 22:59:12.0033119 +0200 CEST m=+0.096740401
|
||||
|
||||
package ioeither
|
||||
|
||||
|
79
ioeither/generic/bind.go
Normal file
79
ioeither/generic/bind.go
Normal 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)
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:09.4489926 +0200 CEST m=+0.129900401
|
||||
// 2023-08-17 22:59:12.0246635 +0200 CEST m=+0.118092001
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:12.1021634 +0200 CEST m=+0.041956701
|
||||
// 2023-08-17 22:59:14.0582736 +0200 CEST m=+0.248503201
|
||||
|
||||
package iooption
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:12.1210201 +0200 CEST m=+0.060813401
|
||||
// 2023-08-17 22:59:14.0642289 +0200 CEST m=+0.254458501
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
53
iterator/stateless/generic/reflect.go
Normal file
53
iterator/stateless/generic/reflect.go
Normal 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)
|
||||
}
|
27
iterator/stateless/reflect.go
Normal file
27
iterator/stateless/reflect.go
Normal 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)
|
||||
}
|
38
iterator/stateless/reflect_test.go
Normal file
38
iterator/stateless/reflect_test.go
Normal 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)
|
||||
}
|
@@ -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
38
lazy/example_lazy_test.go
Normal 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
|
||||
}
|
@@ -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
23
number/integer/string.go
Normal 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
|
||||
)
|
@@ -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
|
||||
})
|
||||
}
|
||||
|
@@ -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,
|
||||
)
|
||||
}
|
||||
|
@@ -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
|
||||
})
|
||||
}
|
||||
|
@@ -24,16 +24,30 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
// Mul is a curried function used to add two numbers
|
||||
func Mul[T Number](coeff T) func(T) T {
|
||||
return func(value T) T {
|
||||
return coeff * value
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -67,7 +67,7 @@ func Modify[S, A any](f func(A) A) func(Iso[S, A]) func(S) S {
|
||||
}
|
||||
|
||||
// Wrap wraps the value
|
||||
func Unwrap[S, A any](s S) func(Iso[S, A]) A {
|
||||
func Unwrap[A, S any](s S) func(Iso[S, A]) A {
|
||||
return func(sa Iso[S, A]) A {
|
||||
return sa.Get(s)
|
||||
}
|
||||
@@ -81,8 +81,8 @@ func Wrap[S, A any](a A) func(Iso[S, A]) S {
|
||||
}
|
||||
|
||||
// From wraps the value
|
||||
func To[S, A any](s S) func(Iso[S, A]) A {
|
||||
return Unwrap[S, A](s)
|
||||
func To[A, S any](s S) func(Iso[S, A]) A {
|
||||
return Unwrap[A, S](s)
|
||||
}
|
||||
|
||||
// To unwraps the value
|
||||
|
@@ -121,7 +121,7 @@ func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
|
||||
// the getter returns an `Option[B]` because the container `A` could already be an option
|
||||
// if the setter is invoked with `Some[B]` then the value of `B` will be set, potentially on a default value of `A` if `A` did not exist
|
||||
// if the setter is invoked with `None[B]` then the container `A` is reset to `None[A]` because this is the only way to remove `B`
|
||||
func ComposeOption[S, A, B any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
||||
func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
||||
defa := F.Constant(defaultA)
|
||||
return func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
||||
foldab := O.Fold(O.None[B], F.Flow2(ab.Get, O.Some[B]))
|
||||
@@ -172,7 +172,7 @@ func ComposeOption[S, A, B any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.O
|
||||
// if the setter is called with `Some[B]` and `A` does not exist, the default of 'A' is updated with `B`
|
||||
// if the setter is called with `None[B]` and `A` does not exist this is the identity operation on 'S'
|
||||
// if the setter is called with `None[B]` and `A` does exist, 'B' is removed from 'A'
|
||||
func ComposeOptions[S, A, B any](defaultA A) func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
||||
func ComposeOptions[S, B, A any](defaultA A) func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
||||
defa := F.Constant(defaultA)
|
||||
noops := F.Constant(F.Identity[S])
|
||||
noneb := O.None[B]()
|
||||
|
@@ -209,7 +209,7 @@ func TestComposeOption(t *testing.T) {
|
||||
// compose lenses
|
||||
lens := F.Pipe1(
|
||||
inner,
|
||||
ComposeOption[Outer, *Inner, int](defaultInner)(value),
|
||||
ComposeOption[Outer, int](defaultInner)(value),
|
||||
)
|
||||
outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}}
|
||||
// the checks
|
||||
@@ -235,7 +235,7 @@ func TestComposeOptions(t *testing.T) {
|
||||
// compose lenses
|
||||
lens := F.Pipe1(
|
||||
inner,
|
||||
ComposeOptions[OuterOpt, *InnerOpt, *int](defaultInner)(value),
|
||||
ComposeOptions[OuterOpt, *int](defaultInner)(value),
|
||||
)
|
||||
// additional settings
|
||||
defaultValue2 := 2
|
||||
|
@@ -24,7 +24,7 @@ import (
|
||||
)
|
||||
|
||||
// AtRecord returns a lens that focusses on a value in a record
|
||||
func AtRecord[M ~map[K]V, K comparable, V any](key K) L.Lens[M, O.Option[V]] {
|
||||
func AtRecord[M ~map[K]V, V any, K comparable](key K) L.Lens[M, O.Option[V]] {
|
||||
addKey := F.Bind1of2(RR.UpsertAt[M, K, V])(key)
|
||||
delKey := F.Bind1of1(RR.DeleteAt[M, K, V])(key)
|
||||
fold := O.Fold(
|
||||
@@ -44,6 +44,6 @@ func AtRecord[M ~map[K]V, K comparable, V any](key K) L.Lens[M, O.Option[V]] {
|
||||
}
|
||||
|
||||
// AtKey returns a `Lens` focused on a required key of a `ReadonlyRecord`
|
||||
func AtKey[M ~map[K]V, S any, K comparable, V any](key K) func(sa L.Lens[S, M]) L.Lens[S, O.Option[V]] {
|
||||
func AtKey[M ~map[K]V, S any, V any, K comparable](key K) func(sa L.Lens[S, M]) L.Lens[S, O.Option[V]] {
|
||||
return L.Compose[S](AtRecord[M](key))
|
||||
}
|
||||
|
@@ -22,11 +22,11 @@ import (
|
||||
)
|
||||
|
||||
// AtRecord returns a lens that focusses on a value in a record
|
||||
func AtRecord[K comparable, V any](key K) L.Lens[map[K]V, O.Option[V]] {
|
||||
func AtRecord[V any, K comparable](key K) L.Lens[map[K]V, O.Option[V]] {
|
||||
return G.AtRecord[map[K]V](key)
|
||||
}
|
||||
|
||||
// AtKey returns a `Lens` focused on a required key of a `ReadonlyRecord`
|
||||
func AtKey[S any, K comparable, V any](key K) func(sa L.Lens[S, map[K]V]) L.Lens[S, O.Option[V]] {
|
||||
func AtKey[S any, V any, K comparable](key K) func(sa L.Lens[S, map[K]V]) L.Lens[S, O.Option[V]] {
|
||||
return G.AtKey[map[K]V, S](key)
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ type (
|
||||
func TestAtKey(t *testing.T) {
|
||||
sa := F.Pipe1(
|
||||
L.Id[S](),
|
||||
AtKey[S, string, int]("a"),
|
||||
AtKey[S, int]("a"),
|
||||
)
|
||||
|
||||
assert.Equal(t, O.Some(1), sa.Get(S{"a": 1}))
|
||||
|
@@ -198,7 +198,7 @@ func TestOuterLensLaws(t *testing.T) {
|
||||
eqValue := EQT.Eq[int]()
|
||||
eqOptValue := O.Eq(eqValue)
|
||||
// lens to access a value from outer
|
||||
valueFromOuter := L.ComposeOption[*Outer, *Inner, int](&defaultInner)(valueLens)(outerLens)
|
||||
valueFromOuter := L.ComposeOption[*Outer, int](&defaultInner)(valueLens)(outerLens)
|
||||
// try to access the value, this should get an option
|
||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
||||
// update the object
|
||||
@@ -234,7 +234,7 @@ func TestOuterOptLensLaws(t *testing.T) {
|
||||
valueFromOuter := F.Pipe3(
|
||||
valueOptLens,
|
||||
LI.Compose[*InnerOpt](intIso),
|
||||
L.ComposeOptions[*OuterOpt, *InnerOpt, int](&defaultInnerOpt),
|
||||
L.ComposeOptions[*OuterOpt, int](&defaultInnerOpt),
|
||||
I.Ap[L.Lens[*OuterOpt, O.Option[int]]](outerOptLens),
|
||||
)
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:14.9503867 +0200 CEST m=+0.020206701
|
||||
// 2023-08-17 22:59:16.3450991 +0200 CEST m=+0.104894201
|
||||
|
||||
package option
|
||||
|
||||
|
38
option/ord.go
Normal file
38
option/ord.go
Normal 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
46
option/ord_test.go
Normal 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")))
|
||||
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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))
|
||||
}
|
||||
}
|
||||
|
@@ -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],
|
||||
|
19
ord/ord.go
19
ord/ord.go
@@ -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 {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:23.1396887 +0200 CEST m=+0.020805301
|
||||
// 2023-08-17 22:59:18.4448291 +0200 CEST m=+0.161369001
|
||||
|
||||
package reader
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:23.1402159 +0200 CEST m=+0.021332501
|
||||
// 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]
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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))
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:26.9546728 +0200 CEST m=+0.032178301
|
||||
// 2023-08-17 22:59:21.3704716 +0200 CEST m=+0.110126101
|
||||
|
||||
package readerioeither
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:26.9556725 +0200 CEST m=+0.033178001
|
||||
// 2023-08-17 22:59:21.3811525 +0200 CEST m=+0.120807001
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
|
@@ -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))
|
||||
}),
|
||||
)
|
||||
|
@@ -186,6 +186,13 @@ 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 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] {
|
||||
n := O.None[V]()
|
||||
return func(m M) O.Option[V] {
|
||||
|
@@ -98,10 +98,15 @@ func MapRefWithIndex[K comparable, V, R any](f func(K, *V) R) func(map[K]V) map[
|
||||
}
|
||||
|
||||
// Lookup returns the entry for a key in a map if it exists
|
||||
func Lookup[K comparable, V any](k K) func(map[K]V) O.Option[V] {
|
||||
func Lookup[V any, K comparable](k K) func(map[K]V) O.Option[V] {
|
||||
return G.Lookup[map[K]V](k)
|
||||
}
|
||||
|
||||
// MonadLookup returns the entry for a key in a map if it exists
|
||||
func MonadLookup[V any, K comparable](m map[K]V, k K) O.Option[V] {
|
||||
return G.MonadLookup[map[K]V](m, k)
|
||||
}
|
||||
|
||||
// Has tests if a key is contained in a map
|
||||
func Has[K comparable, V any](k K, r map[K]V) bool {
|
||||
return G.Has(k, r)
|
||||
|
@@ -71,8 +71,8 @@ func TestLookup(t *testing.T) {
|
||||
"b": "b",
|
||||
"c": "c",
|
||||
}
|
||||
assert.Equal(t, O.Some("a"), Lookup[string, string]("a")(data))
|
||||
assert.Equal(t, O.None[string](), Lookup[string, string]("a1")(data))
|
||||
assert.Equal(t, O.Some("a"), Lookup[string]("a")(data))
|
||||
assert.Equal(t, O.None[string](), Lookup[string]("a1")(data))
|
||||
}
|
||||
|
||||
func TestFilterChain(t *testing.T) {
|
||||
|
31
reflect/generic/reflect.go
Normal file
31
reflect/generic/reflect.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
R "reflect"
|
||||
)
|
||||
|
||||
func Map[GA ~[]A, A any](f func(R.Value) A) func(R.Value) GA {
|
||||
return func(val R.Value) GA {
|
||||
l := val.Len()
|
||||
res := make(GA, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
res[i] = f(val.Index(i))
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
42
reflect/reflect.go
Normal file
42
reflect/reflect.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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 (
|
||||
R "reflect"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
G "github.com/IBM/fp-go/reflect/generic"
|
||||
)
|
||||
|
||||
func ReduceWithIndex[A any](f func(int, A, R.Value) A, initial A) func(R.Value) A {
|
||||
return func(val R.Value) A {
|
||||
count := val.Len()
|
||||
current := initial
|
||||
for i := 0; i < count; i++ {
|
||||
current = f(i, current, val.Index(i))
|
||||
}
|
||||
return current
|
||||
}
|
||||
}
|
||||
|
||||
func Reduce[A any](f func(A, R.Value) A, initial A) func(R.Value) A {
|
||||
return ReduceWithIndex(F.Ignore1of3[int](f), initial)
|
||||
}
|
||||
|
||||
func Map[A any](f func(R.Value) A) func(R.Value) []A {
|
||||
return G.Map[[]A](f)
|
||||
}
|
6
samples/mostly-adequate/README.md
Normal file
6
samples/mostly-adequate/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Mostly Adequate: fp-go Companion Guide
|
||||
|
||||
This resource is meant to serve as a go "companion" resource to Professor [Frisby's Mostly Adequate Guide](https://github.com/MostlyAdequate/mostly-adequate-guide).
|
||||
|
||||
It is a port of the [mostly-adequate-fp-ts](https://github.com/ChuckJonas/mostly-adequate-fp-ts/) book.
|
||||
|
47
samples/mostly-adequate/chapter01_test.go
Normal file
47
samples/mostly-adequate/chapter01_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mostlyadequate
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Flock struct {
|
||||
Seagulls int
|
||||
}
|
||||
|
||||
func MakeFlock(n int) Flock {
|
||||
return Flock{Seagulls: n}
|
||||
}
|
||||
|
||||
func (f *Flock) Conjoin(other *Flock) *Flock {
|
||||
f.Seagulls += other.Seagulls
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Flock) Breed(other *Flock) *Flock {
|
||||
f.Seagulls = f.Seagulls * other.Seagulls
|
||||
return f
|
||||
}
|
||||
|
||||
func Example_flock() {
|
||||
|
||||
flockA := MakeFlock(4)
|
||||
flockB := MakeFlock(2)
|
||||
flockC := MakeFlock(0)
|
||||
|
||||
fmt.Println(flockA.Conjoin(&flockC).Breed(&flockB).Conjoin(flockA.Breed(&flockB)).Seagulls)
|
||||
|
||||
// Output: 32
|
||||
}
|
@@ -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 mostlyadequate
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Hi(name string) string {
|
||||
return fmt.Sprintf("Hi %s", name)
|
||||
}
|
||||
|
||||
func Greeting(name string) string {
|
||||
return Hi(name)
|
||||
}
|
||||
|
||||
func Example_greeting() {
|
||||
// functions are first class objects
|
||||
greet := Hi
|
||||
|
||||
fmt.Println(Greeting("times"))
|
||||
fmt.Println(greet("times"))
|
||||
|
||||
// Output:
|
||||
// Hi times
|
||||
// Hi times
|
||||
}
|
83
samples/mostly-adequate/chapter04_currying_test.go
Normal file
83
samples/mostly-adequate/chapter04_currying_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
I "github.com/IBM/fp-go/number/integer"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
var (
|
||||
Match = F.Curry2((*regexp.Regexp).FindStringSubmatch)
|
||||
Matches = F.Curry2((*regexp.Regexp).MatchString)
|
||||
Split = F.Curry2(F.Bind3of3((*regexp.Regexp).Split)(-1))
|
||||
|
||||
Add = N.Add[int]
|
||||
ToString = I.ToString
|
||||
ToLower = strings.ToLower
|
||||
ToUpper = strings.ToUpper
|
||||
Concat = F.Curry2(S.Monoid.Concat)
|
||||
)
|
||||
|
||||
// Replace cannot be generated via [F.Curry3] because the order of parameters does not match our desired curried order
|
||||
func Replace(search *regexp.Regexp) func(replace string) func(s string) string {
|
||||
return func(replace string) func(s string) string {
|
||||
return func(s string) string {
|
||||
return search.ReplaceAllString(s, replace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Example_solution04A() {
|
||||
// words :: String -> [String]
|
||||
words := Split(regexp.MustCompile(` `))
|
||||
|
||||
fmt.Println(words("Jingle bells Batman smells"))
|
||||
|
||||
// Output:
|
||||
// [Jingle bells Batman smells]
|
||||
}
|
||||
|
||||
func Example_solution04B() {
|
||||
// filterQs :: [String] -> [String]
|
||||
filterQs := A.Filter(Matches(regexp.MustCompile(`q`)))
|
||||
|
||||
fmt.Println(filterQs(A.From("quick", "camels", "quarry", "over", "quails")))
|
||||
|
||||
// Output:
|
||||
// [quick quarry quails]
|
||||
}
|
||||
|
||||
func Example_solution04C() {
|
||||
|
||||
keepHighest := N.Max[int]
|
||||
|
||||
// max :: [Number] -> Number
|
||||
max := A.Reduce(keepHighest, math.MinInt)
|
||||
|
||||
fmt.Println(max(A.From(323, 523, 554, 123, 5234)))
|
||||
|
||||
// Output:
|
||||
// 5234
|
||||
}
|
110
samples/mostly-adequate/chapter05_composing_test.go
Normal file
110
samples/mostly-adequate/chapter05_composing_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
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"
|
||||
)
|
||||
|
||||
var (
|
||||
Exclaim = S.Format[string]("%s!")
|
||||
Shout = F.Flow2(ToUpper, Exclaim)
|
||||
Dasherize = F.Flow4(
|
||||
Replace(regexp.MustCompile(`\s{2,}`))(" "),
|
||||
Split(regexp.MustCompile(` `)),
|
||||
A.Map(ToLower),
|
||||
A.Intercalate(S.Monoid)("-"),
|
||||
)
|
||||
)
|
||||
|
||||
func Example_shout() {
|
||||
fmt.Println(Shout("send in the clowns"))
|
||||
|
||||
// Output: SEND IN THE CLOWNS!
|
||||
}
|
||||
|
||||
func Example_dasherize() {
|
||||
fmt.Println(Dasherize("The world is a vampire"))
|
||||
|
||||
// Output: the-world-is-a-vampire
|
||||
}
|
||||
|
||||
func Example_pipe() {
|
||||
output := F.Pipe2(
|
||||
"send in the clowns",
|
||||
ToUpper,
|
||||
Exclaim,
|
||||
)
|
||||
|
||||
fmt.Println(output)
|
||||
|
||||
// Output: SEND IN THE CLOWNS!
|
||||
}
|
||||
|
||||
func Example_solution05A() {
|
||||
IsLastInStock := F.Flow2(
|
||||
A.Last[Car],
|
||||
O.Map(Car.getInStock),
|
||||
)
|
||||
|
||||
fmt.Println(IsLastInStock(Cars[0:3]))
|
||||
fmt.Println(IsLastInStock(Cars[3:]))
|
||||
|
||||
// Output:
|
||||
// Some[bool](true)
|
||||
// Some[bool](false)
|
||||
}
|
||||
|
||||
func Example_solution05B() {
|
||||
// averageDollarValue :: [Car] -> Int
|
||||
averageDollarValue := F.Flow2(
|
||||
A.Map(Car.getDollarValue),
|
||||
average,
|
||||
)
|
||||
|
||||
fmt.Println(averageDollarValue(Cars))
|
||||
|
||||
// Output:
|
||||
// 790700
|
||||
}
|
||||
|
||||
func Example_solution05C() {
|
||||
// order by horsepower
|
||||
ordByHorsepower := ord.Contramap(Car.getHorsepower)(I.Ord)
|
||||
|
||||
// fastestCar :: [Car] -> Option[String]
|
||||
fastestCar := F.Flow3(
|
||||
A.Sort(ordByHorsepower),
|
||||
A.Last[Car],
|
||||
O.Map(F.Flow2(
|
||||
Car.getName,
|
||||
S.Format[string]("%s is the fastest"),
|
||||
)),
|
||||
)
|
||||
|
||||
fmt.Println(fastestCar(Cars))
|
||||
|
||||
// Output:
|
||||
// Some[string](Aston Martin One-77 is the fastest)
|
||||
}
|
115
samples/mostly-adequate/chapter06_exampleapplication_test.go
Normal file
115
samples/mostly-adequate/chapter06_exampleapplication_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
J "github.com/IBM/fp-go/json"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
|
||||
R "github.com/IBM/fp-go/context/readerioeither"
|
||||
H "github.com/IBM/fp-go/context/readerioeither/http"
|
||||
)
|
||||
|
||||
type (
|
||||
FlickrMedia struct {
|
||||
Link string `json:"m"`
|
||||
}
|
||||
|
||||
FlickrItem struct {
|
||||
Media FlickrMedia `json:"media"`
|
||||
}
|
||||
|
||||
FlickrFeed struct {
|
||||
Items []FlickrItem `json:"items"`
|
||||
}
|
||||
)
|
||||
|
||||
func (f FlickrMedia) getLink() string {
|
||||
return f.Link
|
||||
}
|
||||
|
||||
func (f FlickrItem) getMedia() FlickrMedia {
|
||||
return f.Media
|
||||
}
|
||||
|
||||
func (f FlickrFeed) getItems() []FlickrItem {
|
||||
return f.Items
|
||||
}
|
||||
|
||||
func Example_application() {
|
||||
// pure
|
||||
host := "api.flickr.com"
|
||||
path := "/services/feeds/photos_public.gne"
|
||||
query := S.Format[string]("?tags=%s&format=json&jsoncallback=?")
|
||||
url := F.Flow2(
|
||||
query,
|
||||
S.Format[string](fmt.Sprintf("https://%s%s%%s", host, path)),
|
||||
)
|
||||
// flick returns jsonP, we extract the JSON body, this is handled by jquery in the original code
|
||||
sanitizeJsonP := Replace(regexp.MustCompile(`(?s)^\s*\((.*)\)\s*$`))("$1")
|
||||
// parse jsonP
|
||||
parseJsonP := F.Flow3(
|
||||
sanitizeJsonP,
|
||||
S.ToBytes,
|
||||
J.Unmarshal[FlickrFeed],
|
||||
)
|
||||
// markup
|
||||
img := S.Format[string]("<img src='%s'/>")
|
||||
// lenses
|
||||
mediaUrl := F.Flow2(
|
||||
FlickrItem.getMedia,
|
||||
FlickrMedia.getLink,
|
||||
)
|
||||
mediaUrls := F.Flow2(
|
||||
FlickrFeed.getItems,
|
||||
A.Map(mediaUrl),
|
||||
)
|
||||
images := F.Flow2(
|
||||
mediaUrls,
|
||||
A.Map(img),
|
||||
)
|
||||
|
||||
client := H.MakeClient(http.DefaultClient)
|
||||
|
||||
// func(string) R.ReaderIOEither[[]string]
|
||||
app := F.Flow5(
|
||||
url,
|
||||
H.MakeGetRequest,
|
||||
H.ReadText(client),
|
||||
R.ChainEitherK(parseJsonP),
|
||||
R.Map(images),
|
||||
)
|
||||
|
||||
// R.ReaderIOEither[[]string]
|
||||
// this is the managed effect that can be called to download and render the images
|
||||
catImageEffect := app("cats")
|
||||
|
||||
// impure, actually executes the effect
|
||||
catImages := catImageEffect(context.TODO())()
|
||||
fmt.Println(E.IsRight(catImages))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
|
||||
}
|
276
samples/mostly-adequate/chapter08_tupperware_test.go
Normal file
276
samples/mostly-adequate/chapter08_tupperware_test.go
Normal file
@@ -0,0 +1,276 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
"github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
I "github.com/IBM/fp-go/identity"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/IBM/fp-go/ord"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
Balance float32
|
||||
}
|
||||
|
||||
func MakeAccount(b float32) Account {
|
||||
return Account{Balance: b}
|
||||
}
|
||||
|
||||
func getBalance(a Account) float32 {
|
||||
return a.Balance
|
||||
}
|
||||
|
||||
type (
|
||||
Chapter08User struct {
|
||||
Id int
|
||||
Name string
|
||||
Active bool
|
||||
Saved bool
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
albert08 = Chapter08User{
|
||||
Id: 1,
|
||||
Active: true,
|
||||
Name: "Albert",
|
||||
}
|
||||
|
||||
gary08 = Chapter08User{
|
||||
Id: 2,
|
||||
Active: false,
|
||||
Name: "Gary",
|
||||
}
|
||||
|
||||
theresa08 = Chapter08User{
|
||||
Id: 3,
|
||||
Active: true,
|
||||
Name: "Theresa",
|
||||
}
|
||||
|
||||
yi08 = Chapter08User{Id: 4, Name: "Yi", Active: true}
|
||||
)
|
||||
|
||||
func (u Chapter08User) getName() string {
|
||||
return u.Name
|
||||
}
|
||||
|
||||
func (u Chapter08User) isActive() bool {
|
||||
return u.Active
|
||||
}
|
||||
|
||||
var (
|
||||
ordFloat32 = ord.FromStrictCompare[float32]()
|
||||
UpdateLedger = F.Identity[Account]
|
||||
RemainingBalance = F.Flow2(
|
||||
getBalance,
|
||||
S.Format[float32]("Your balance is $%0.2f"),
|
||||
)
|
||||
FinishTransaction = F.Flow2(
|
||||
UpdateLedger,
|
||||
RemainingBalance,
|
||||
)
|
||||
getTwenty = F.Flow2(
|
||||
Withdraw(20),
|
||||
O.Fold(F.Constant("You're broke!"), FinishTransaction),
|
||||
)
|
||||
|
||||
// showWelcome :: User -> String
|
||||
showWelcome = F.Flow2(
|
||||
Chapter08User.getName,
|
||||
S.Format[string]("Welcome %s"),
|
||||
)
|
||||
|
||||
// checkActive :: User -> Either error User
|
||||
checkActive = E.FromPredicate(Chapter08User.isActive, F.Constant1[Chapter08User](fmt.Errorf("Your account is not active")))
|
||||
|
||||
// validateUser :: (User -> Either String ()) -> User -> Either String User
|
||||
validateUser = F.Curry2(func(validate func(Chapter08User) E.Either[error, any], user Chapter08User) E.Either[error, Chapter08User] {
|
||||
return F.Pipe2(
|
||||
user,
|
||||
validate,
|
||||
E.MapTo[error, any](user),
|
||||
)
|
||||
})
|
||||
|
||||
// save :: User -> IOEither error User
|
||||
save = func(user Chapter08User) IOE.IOEither[error, Chapter08User] {
|
||||
return IOE.FromIO[error](func() Chapter08User {
|
||||
var u = user
|
||||
u.Saved = true
|
||||
return u
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
func Withdraw(amount float32) func(account Account) O.Option[Account] {
|
||||
|
||||
return F.Flow3(
|
||||
getBalance,
|
||||
O.FromPredicate(ord.Geq(ordFloat32)(amount)),
|
||||
O.Map(F.Flow2(
|
||||
N.Add(-amount),
|
||||
MakeAccount,
|
||||
)))
|
||||
}
|
||||
|
||||
type User struct {
|
||||
BirthDate string
|
||||
}
|
||||
|
||||
func getBirthDate(u User) string {
|
||||
return u.BirthDate
|
||||
}
|
||||
|
||||
func MakeUser(d string) User {
|
||||
return User{BirthDate: d}
|
||||
}
|
||||
|
||||
var parseDate = F.Bind1of2(E.Eitherize2(time.Parse))(time.DateOnly)
|
||||
|
||||
func GetAge(now time.Time) func(User) E.Either[error, float64] {
|
||||
return F.Flow3(
|
||||
getBirthDate,
|
||||
parseDate,
|
||||
E.Map[error](F.Flow3(
|
||||
now.Sub,
|
||||
time.Duration.Hours,
|
||||
N.Mul(1/24.0),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
func Example_widthdraw() {
|
||||
fmt.Println(getTwenty(MakeAccount(200)))
|
||||
fmt.Println(getTwenty(MakeAccount(10)))
|
||||
|
||||
// Output:
|
||||
// Your balance is $180.00
|
||||
// You're broke!
|
||||
}
|
||||
|
||||
func Example_getAge() {
|
||||
now, err := time.Parse(time.DateOnly, "2023-09-01")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(GetAge(now)(MakeUser("2005-12-12")))
|
||||
fmt.Println(GetAge(now)(MakeUser("July 4, 2001")))
|
||||
|
||||
fortune := F.Flow3(
|
||||
N.Add(365.0),
|
||||
S.Format[float64]("%0.0f"),
|
||||
Concat("If you survive, you will be "),
|
||||
)
|
||||
|
||||
zoltar := F.Flow3(
|
||||
GetAge(now),
|
||||
E.Map[error](fortune),
|
||||
E.GetOrElse(errors.ToString),
|
||||
)
|
||||
|
||||
fmt.Println(zoltar(MakeUser("2005-12-12")))
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, float64](6472)
|
||||
// Left[*time.ParseError, float64](parsing time "July 4, 2001" as "2006-01-02": cannot parse "July 4, 2001" as "2006")
|
||||
// If you survive, you will be 6837
|
||||
}
|
||||
|
||||
func Example_solution08A() {
|
||||
incrF := I.Map(N.Add(1))
|
||||
|
||||
fmt.Println(incrF(I.Of(2)))
|
||||
|
||||
// Output: 3
|
||||
}
|
||||
|
||||
func Example_solution08B() {
|
||||
// initial :: User -> Option rune
|
||||
initial := F.Flow3(
|
||||
Chapter08User.getName,
|
||||
S.ToRunes,
|
||||
A.Head[rune],
|
||||
)
|
||||
|
||||
fmt.Println(initial(albert08))
|
||||
|
||||
// Output:
|
||||
// Some[int32](65)
|
||||
}
|
||||
|
||||
func Example_solution08C() {
|
||||
|
||||
// eitherWelcome :: User -> Either String String
|
||||
eitherWelcome := F.Flow2(
|
||||
checkActive,
|
||||
E.Map[error](showWelcome),
|
||||
)
|
||||
|
||||
fmt.Println(eitherWelcome(gary08))
|
||||
fmt.Println(eitherWelcome(theresa08))
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, string](Your account is not active)
|
||||
// Right[<nil>, string](Welcome Theresa)
|
||||
}
|
||||
|
||||
func Example_solution08D() {
|
||||
|
||||
// // validateName :: User -> Either String ()
|
||||
validateName := F.Flow3(
|
||||
Chapter08User.getName,
|
||||
E.FromPredicate(F.Flow2(
|
||||
S.Size,
|
||||
ord.Gt(ord.FromStrictCompare[int]())(3),
|
||||
), errors.OnSome[string]("Your name %s is larger than 3 characters")),
|
||||
E.Map[error](F.ToAny[string]),
|
||||
)
|
||||
|
||||
saveAndWelcome := F.Flow2(
|
||||
save,
|
||||
IOE.Map[error](showWelcome),
|
||||
)
|
||||
|
||||
register := F.Flow3(
|
||||
validateUser(validateName),
|
||||
IOE.FromEither[error, Chapter08User],
|
||||
IOE.Chain(saveAndWelcome),
|
||||
)
|
||||
|
||||
fmt.Println(validateName(gary08))
|
||||
fmt.Println(validateName(yi08))
|
||||
|
||||
fmt.Println(register(albert08)())
|
||||
fmt.Println(register(yi08)())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, string](Gary)
|
||||
// Left[*errors.errorString, <nil>](Your name Yi is larger than 3 characters)
|
||||
// Right[<nil>, string](Welcome Albert)
|
||||
// Left[*errors.errorString, string](Your name Yi is larger than 3 characters)
|
||||
}
|
186
samples/mostly-adequate/chapter09_monadiconions_test.go
Normal file
186
samples/mostly-adequate/chapter09_monadiconions_test.go
Normal file
@@ -0,0 +1,186 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
"github.com/IBM/fp-go/errors"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/io"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
type (
|
||||
Street struct {
|
||||
Name string
|
||||
Number int
|
||||
}
|
||||
|
||||
Address struct {
|
||||
Street Street
|
||||
Postcode string
|
||||
}
|
||||
|
||||
AddressBook struct {
|
||||
Addresses []Address
|
||||
}
|
||||
|
||||
Chapter09User struct {
|
||||
Id int
|
||||
Name string
|
||||
Address Address
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
albert09 = Chapter09User{
|
||||
Id: 1,
|
||||
Name: "Albert",
|
||||
Address: Address{
|
||||
Street: Street{
|
||||
Number: 22,
|
||||
Name: "Walnut St",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
gary09 = Chapter09User{
|
||||
Id: 2,
|
||||
Name: "Gary",
|
||||
Address: Address{
|
||||
Street: Street{
|
||||
Number: 14,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
theresa09 = Chapter09User{
|
||||
Id: 3,
|
||||
Name: "Theresa",
|
||||
}
|
||||
)
|
||||
|
||||
func (ab AddressBook) getAddresses() []Address {
|
||||
return ab.Addresses
|
||||
}
|
||||
|
||||
func (s Address) getStreet() Street {
|
||||
return s.Street
|
||||
}
|
||||
|
||||
func (s Street) getName() string {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
func (u Chapter09User) getAddress() Address {
|
||||
return u.Address
|
||||
}
|
||||
|
||||
var (
|
||||
FirstAddressStreet = F.Flow3(
|
||||
AddressBook.getAddresses,
|
||||
A.Head[Address],
|
||||
O.Map(Address.getStreet),
|
||||
)
|
||||
|
||||
// getFile :: IO String
|
||||
getFile = io.Of("/home/mostly-adequate/ch09.md")
|
||||
|
||||
// pureLog :: String -> IO ()
|
||||
pureLog = io.Logf[string]("%s")
|
||||
|
||||
// addToMailingList :: Email -> IOEither([Email])
|
||||
addToMailingList = F.Flow2(
|
||||
A.Of[string],
|
||||
IOE.Of[error, []string],
|
||||
)
|
||||
|
||||
// validateEmail :: Email -> Either error Email
|
||||
validateEmail = E.FromPredicate(Matches(regexp.MustCompile(`\S+@\S+\.\S+`)), errors.OnSome[string]("email %s is invalid"))
|
||||
|
||||
// emailBlast :: [Email] -> IO ()
|
||||
emailBlast = F.Flow2(
|
||||
A.Intercalate(S.Monoid)(","),
|
||||
IOE.Of[error, string],
|
||||
)
|
||||
)
|
||||
|
||||
func Example_street() {
|
||||
s := FirstAddressStreet(AddressBook{
|
||||
Addresses: A.From(Address{Street: Street{Name: "Mulburry", Number: 8402}, Postcode: "WC2N"}),
|
||||
})
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// Some[mostlyadequate.Street]({Mulburry 8402})
|
||||
}
|
||||
|
||||
func Example_solution09A() {
|
||||
// // getStreetName :: User -> Maybe String
|
||||
getStreetName := F.Flow4(
|
||||
Chapter09User.getAddress,
|
||||
Address.getStreet,
|
||||
Street.getName,
|
||||
O.FromPredicate(S.IsNonEmpty),
|
||||
)
|
||||
|
||||
fmt.Println(getStreetName(albert09))
|
||||
fmt.Println(getStreetName(gary09))
|
||||
fmt.Println(getStreetName(theresa09))
|
||||
|
||||
// Output:
|
||||
// Some[string](Walnut St)
|
||||
// None[string]
|
||||
// None[string]
|
||||
|
||||
}
|
||||
|
||||
func Example_solution09B() {
|
||||
logFilename := F.Flow2(
|
||||
io.Map(path.Base),
|
||||
io.ChainFirst(pureLog),
|
||||
)
|
||||
|
||||
fmt.Println(logFilename(getFile)())
|
||||
|
||||
// Output:
|
||||
// ch09.md
|
||||
}
|
||||
|
||||
func Example_solution09C() {
|
||||
|
||||
// // joinMailingList :: Email -> Either String (IO ())
|
||||
joinMailingList := F.Flow4(
|
||||
validateEmail,
|
||||
IOE.FromEither[error, string],
|
||||
IOE.Chain(addToMailingList),
|
||||
IOE.Chain(emailBlast),
|
||||
)
|
||||
|
||||
fmt.Println(joinMailingList("sleepy@grandpa.net")())
|
||||
fmt.Println(joinMailingList("notanemail")())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, string](sleepy@grandpa.net)
|
||||
// Left[*errors.errorString, string](email notanemail is invalid)
|
||||
}
|
183
samples/mostly-adequate/chapter10_applicativefunctor_test.go
Normal file
183
samples/mostly-adequate/chapter10_applicativefunctor_test.go
Normal file
@@ -0,0 +1,183 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
R "github.com/IBM/fp-go/context/readerioeither"
|
||||
H "github.com/IBM/fp-go/context/readerioeither/http"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IOO "github.com/IBM/fp-go/iooption"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
M "github.com/IBM/fp-go/record"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
type (
|
||||
PostItem struct {
|
||||
UserId uint `json:"userId"`
|
||||
Id uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
Player struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
LocalStorage = map[string]Player
|
||||
)
|
||||
|
||||
var (
|
||||
playerAlbert = Player{
|
||||
Id: 1,
|
||||
Name: "Albert",
|
||||
}
|
||||
playerTheresa = Player{
|
||||
Id: 2,
|
||||
Name: "Theresa",
|
||||
}
|
||||
localStorage = LocalStorage{
|
||||
"player1": playerAlbert,
|
||||
"player2": playerTheresa,
|
||||
}
|
||||
|
||||
// getFromCache :: String -> IO User
|
||||
getFromCache = func(name string) IOO.IOOption[Player] {
|
||||
return func() O.Option[Player] {
|
||||
return M.MonadLookup(localStorage, name)
|
||||
}
|
||||
}
|
||||
|
||||
// game :: User -> User -> String
|
||||
game = F.Curry2(func(a, b Player) string {
|
||||
return fmt.Sprintf("%s vs %s", a.Name, b.Name)
|
||||
})
|
||||
)
|
||||
|
||||
func (player Player) getName() string {
|
||||
return player.Name
|
||||
}
|
||||
|
||||
func (player Player) getId() int {
|
||||
return player.Id
|
||||
}
|
||||
|
||||
func (item PostItem) getTitle() string {
|
||||
return item.Title
|
||||
}
|
||||
|
||||
func idxToUrl(idx int) string {
|
||||
return fmt.Sprintf("https://jsonplaceholder.typicode.com/posts/%d", idx+1)
|
||||
}
|
||||
|
||||
func renderString(destinations string) func(string) string {
|
||||
return func(events string) string {
|
||||
return fmt.Sprintf("<div>Destinations: [%s], Events: [%s]</div>", destinations, events)
|
||||
}
|
||||
}
|
||||
|
||||
func Example_renderPage() {
|
||||
// prepare the http client
|
||||
client := H.MakeClient(http.DefaultClient)
|
||||
|
||||
// get returns the title of the nth item from the REST service
|
||||
get := F.Flow4(
|
||||
idxToUrl,
|
||||
H.MakeGetRequest,
|
||||
H.ReadJson[PostItem](client),
|
||||
R.Map(PostItem.getTitle),
|
||||
)
|
||||
|
||||
res := F.Pipe2(
|
||||
R.Of(renderString), // start with a function with 2 unresolved arguments
|
||||
R.Ap[func(string) string](get(1)), // resolve the first argument
|
||||
R.Ap[string](get(2)), // in parallel resolve the second argument
|
||||
)
|
||||
|
||||
// finally invoke in context and start
|
||||
fmt.Println(res(context.TODO())())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, string](<div>Destinations: [qui est esse], Events: [ea molestias quasi exercitationem repellat qui ipsa sit aut]</div>)
|
||||
|
||||
}
|
||||
|
||||
func Example_solution10A() {
|
||||
safeAdd := F.Curry2(func(a, b O.Option[int]) O.Option[int] {
|
||||
return F.Pipe3(
|
||||
N.Add[int],
|
||||
O.Of[func(int) func(int) int],
|
||||
O.Ap[func(int) int](a),
|
||||
O.Ap[int](b),
|
||||
)
|
||||
})
|
||||
|
||||
fmt.Println(safeAdd(O.Of(2))(O.Of(3)))
|
||||
fmt.Println(safeAdd(O.None[int]())(O.Of(3)))
|
||||
fmt.Println(safeAdd(O.Of(2))(O.None[int]()))
|
||||
|
||||
// Output:
|
||||
// Some[int](5)
|
||||
// None[int]
|
||||
// None[int]
|
||||
}
|
||||
|
||||
func Example_solution10B() {
|
||||
|
||||
safeAdd := F.Curry2(T.Untupled2(F.Flow2(
|
||||
O.SequenceTuple2[int, int],
|
||||
O.Map(T.Tupled2(N.MonoidSum[int]().Concat)),
|
||||
)))
|
||||
|
||||
fmt.Println(safeAdd(O.Of(2))(O.Of(3)))
|
||||
fmt.Println(safeAdd(O.None[int]())(O.Of(3)))
|
||||
fmt.Println(safeAdd(O.Of(2))(O.None[int]()))
|
||||
|
||||
// Output:
|
||||
// Some[int](5)
|
||||
// None[int]
|
||||
// None[int]
|
||||
}
|
||||
|
||||
func Example_solution10C() {
|
||||
// startGame :: IO String
|
||||
startGame := F.Pipe2(
|
||||
IOO.Of(game),
|
||||
IOO.Ap[func(Player) string](getFromCache("player1")),
|
||||
IOO.Ap[string](getFromCache("player2")),
|
||||
)
|
||||
|
||||
startGameTupled := F.Pipe2(
|
||||
T.MakeTuple2("player1", "player2"),
|
||||
IOO.TraverseTuple2(getFromCache, getFromCache),
|
||||
IOO.Map(T.Tupled2(func(a, b Player) string {
|
||||
return fmt.Sprintf("%s vs %s", a.Name, b.Name)
|
||||
})),
|
||||
)
|
||||
|
||||
fmt.Println(startGame())
|
||||
fmt.Println(startGameTupled())
|
||||
|
||||
// Output:
|
||||
// Some[string](Albert vs Theresa)
|
||||
// Some[string](Albert vs Theresa)
|
||||
}
|
89
samples/mostly-adequate/chapter11_transformagain_test.go
Normal file
89
samples/mostly-adequate/chapter11_transformagain_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mostlyadequate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
IOE "github.com/IBM/fp-go/ioeither"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
func findUserById(id int) IOE.IOEither[error, Chapter08User] {
|
||||
switch id {
|
||||
case 1:
|
||||
return IOE.Of[error](albert08)
|
||||
case 2:
|
||||
return IOE.Of[error](gary08)
|
||||
case 3:
|
||||
return IOE.Of[error](theresa08)
|
||||
default:
|
||||
return IOE.Left[Chapter08User](fmt.Errorf("user %d not found", id))
|
||||
}
|
||||
}
|
||||
|
||||
func Example_solution11A() {
|
||||
// eitherToMaybe :: Either b a -> Maybe a
|
||||
eitherToMaybe := E.ToOption[error, string]
|
||||
|
||||
fmt.Println(eitherToMaybe(E.Of[error]("one eyed willy")))
|
||||
fmt.Println(eitherToMaybe(E.Left[string](fmt.Errorf("some error"))))
|
||||
|
||||
// Output:
|
||||
// Some[string](one eyed willy)
|
||||
// None[string]
|
||||
}
|
||||
|
||||
func Example_solution11B() {
|
||||
findByNameId := F.Flow2(
|
||||
findUserById,
|
||||
IOE.Map[error](Chapter08User.getName),
|
||||
)
|
||||
|
||||
fmt.Println(findByNameId(1)())
|
||||
fmt.Println(findByNameId(2)())
|
||||
fmt.Println(findByNameId(3)())
|
||||
fmt.Println(findByNameId(4)())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, string](Albert)
|
||||
// Right[<nil>, string](Gary)
|
||||
// Right[<nil>, string](Theresa)
|
||||
// Left[*errors.errorString, string](user 4 not found)
|
||||
}
|
||||
|
||||
func Example_solution11C() {
|
||||
// strToList :: String -> [Char
|
||||
strToList := Split(regexp.MustCompile(``))
|
||||
|
||||
// listToStr :: [Char] -> String
|
||||
listToStr := A.Intercalate(S.Monoid)("")
|
||||
|
||||
sortLetters := F.Flow3(
|
||||
strToList,
|
||||
A.Sort(S.Ord),
|
||||
listToStr,
|
||||
)
|
||||
|
||||
fmt.Println(sortLetters("sortme"))
|
||||
|
||||
// Output:
|
||||
// emorst
|
||||
}
|
98
samples/mostly-adequate/chapter12_traversing_test.go
Normal file
98
samples/mostly-adequate/chapter12_traversing_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// 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 mostlyadequate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
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"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
P "github.com/IBM/fp-go/predicate"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
)
|
||||
|
||||
var (
|
||||
// httpGet :: Route -> Task Error JSON
|
||||
httpGet = F.Flow2(
|
||||
S.Format[string]("json for %s"),
|
||||
IOE.Of[error, string],
|
||||
)
|
||||
|
||||
// routes :: Map Route Route
|
||||
routes = map[string]string{
|
||||
"/": "/",
|
||||
"/about": "/about",
|
||||
}
|
||||
|
||||
// validate :: Player -> Either error Player
|
||||
validatePlayer = E.FromPredicate(P.ContraMap(Player.getName)(S.IsNonEmpty), F.Flow2(Player.getId, errors.OnSome[int]("player %d must have a name")))
|
||||
|
||||
// readfile :: String -> String -> Task Error String
|
||||
readfile = F.Curry2(func(encoding, file string) IOE.IOEither[error, string] {
|
||||
return IOE.Of[error](fmt.Sprintf("content of %s (%s)", file, encoding))
|
||||
})
|
||||
|
||||
// readdir :: String -> Task Error [String]
|
||||
readdir = IOE.Of[error](A.From("file1", "file2", "file3"))
|
||||
)
|
||||
|
||||
func Example_solution12A() {
|
||||
// getJsons :: Map Route Route -> Task Error (Map Route JSON)
|
||||
getJsons := IOE.TraverseRecord[string](httpGet)
|
||||
|
||||
fmt.Println(getJsons(routes)())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, map[string]string](map[/:json for / /about:json for /about])
|
||||
}
|
||||
|
||||
func Example_solution12B() {
|
||||
// startGame :: [Player] -> [Either Error String]
|
||||
startGame := F.Flow2(
|
||||
E.TraverseArray(validatePlayer),
|
||||
E.MapTo[error, []Player]("Game started"),
|
||||
)
|
||||
|
||||
fmt.Println(startGame(A.From(playerAlbert, playerTheresa)))
|
||||
fmt.Println(startGame(A.From(playerAlbert, Player{Id: 4})))
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, string](Game started)
|
||||
// Left[*errors.errorString, string](player 4 must have a name)
|
||||
}
|
||||
|
||||
func Example_solution12C() {
|
||||
traverseO := O.Traverse[string](
|
||||
IOE.Of[error, O.Option[string]],
|
||||
IOE.Map[error, string, O.Option[string]],
|
||||
)
|
||||
|
||||
// readFirst :: String -> Task Error (Maybe String)
|
||||
readFirst := F.Pipe2(
|
||||
readdir,
|
||||
IOE.Map[error](A.Head[string]),
|
||||
IOE.Chain(traverseO(readfile("utf-8"))),
|
||||
)
|
||||
|
||||
fmt.Println(readFirst())
|
||||
|
||||
// Output:
|
||||
// Right[<nil>, option.Option[string]](Some[string](content of file1 (utf-8)))
|
||||
}
|
19
samples/mostly-adequate/doc.go
Normal file
19
samples/mostly-adequate/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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 mostlyadequate is meant to serve as a go "companion" resource to Professor [Frisby's Mostly Adequate Guide].
|
||||
//
|
||||
// [Frisby's Mostly Adequate Guide]: https://github.com/MostlyAdequate/mostly-adequate-guide
|
||||
package mostlyadequate
|
89
samples/mostly-adequate/support.go
Normal file
89
samples/mostly-adequate/support.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mostlyadequate
|
||||
|
||||
import (
|
||||
A "github.com/IBM/fp-go/array"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
)
|
||||
|
||||
type (
|
||||
Car struct {
|
||||
Name string
|
||||
Horsepower int
|
||||
DollarValue float32
|
||||
InStock bool
|
||||
}
|
||||
)
|
||||
|
||||
func (car Car) getInStock() bool {
|
||||
return car.InStock
|
||||
}
|
||||
|
||||
func (car Car) getDollarValue() float32 {
|
||||
return car.DollarValue
|
||||
}
|
||||
|
||||
func (car Car) getHorsepower() int {
|
||||
return car.Horsepower
|
||||
}
|
||||
|
||||
func (car Car) getName() string {
|
||||
return car.Name
|
||||
}
|
||||
|
||||
func average(val []float32) float32 {
|
||||
return F.Pipe2(
|
||||
val,
|
||||
A.Fold(N.MonoidSum[float32]()),
|
||||
N.Div(float32(len(val))),
|
||||
)
|
||||
}
|
||||
|
||||
var (
|
||||
Cars = A.From(Car{
|
||||
Name: "Ferrari FF",
|
||||
Horsepower: 660,
|
||||
DollarValue: 700000,
|
||||
InStock: true,
|
||||
}, Car{
|
||||
Name: "Spyker C12 Zagato",
|
||||
Horsepower: 650,
|
||||
DollarValue: 648000,
|
||||
InStock: false,
|
||||
}, Car{
|
||||
Name: "Jaguar XKR-S",
|
||||
Horsepower: 550,
|
||||
DollarValue: 132000,
|
||||
InStock: true,
|
||||
}, Car{
|
||||
Name: "Audi R8",
|
||||
Horsepower: 525,
|
||||
DollarValue: 114200,
|
||||
InStock: false,
|
||||
}, Car{
|
||||
Name: "Aston Martin One-77",
|
||||
Horsepower: 750,
|
||||
DollarValue: 1850000,
|
||||
InStock: true,
|
||||
}, Car{
|
||||
Name: "Pagani Huayra",
|
||||
Horsepower: 700,
|
||||
DollarValue: 1300000,
|
||||
InStock: false,
|
||||
})
|
||||
)
|
@@ -20,7 +20,7 @@ import (
|
||||
"strings"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/ord"
|
||||
"github.com/IBM/fp-go/ord"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,7 +31,7 @@ var (
|
||||
ToLowerCase = strings.ToLower
|
||||
|
||||
// Ord implements the default ordering for strings
|
||||
Ord = O.FromStrictCompare[string]()
|
||||
Ord = ord.FromStrictCompare[string]()
|
||||
)
|
||||
|
||||
func Eq(left string, right string) bool {
|
||||
@@ -42,6 +42,10 @@ func ToBytes(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
func ToRunes(s string) []rune {
|
||||
return []rune(s)
|
||||
}
|
||||
|
||||
func IsEmpty(s string) bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
65
tuple/gen.go
65
tuple/gen.go
@@ -1,6 +1,6 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-08-11 11:38:30.5947727 +0200 CEST m=+0.033600301
|
||||
// 2023-08-17 22:59:23.6923568 +0200 CEST m=+0.056359401
|
||||
|
||||
package tuple
|
||||
|
||||
@@ -205,6 +205,13 @@ func FromArray1[F1 ~func(R) T1, T1, R any](f1 F1) func(r []R) Tuple1[T1] {
|
||||
}
|
||||
}
|
||||
|
||||
// Push1 creates a [Tuple2] from a [Tuple1] by appending a constant value
|
||||
func Push1[T1, T2 any](value T2) func(Tuple1[T1]) Tuple2[T1, T2] {
|
||||
return func(t Tuple1[T1]) Tuple2[T1, T2] {
|
||||
return MakeTuple2(t.F1, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple2 is a function that converts its 2 parameters into a [Tuple2]
|
||||
func MakeTuple2[T1, T2 any](t1 T1, t2 T2) Tuple2[T1, T2] {
|
||||
return Tuple2[T1, T2]{t1, t2}
|
||||
@@ -315,6 +322,13 @@ func FromArray2[F1 ~func(R) T1, F2 ~func(R) T2, T1, T2, R any](f1 F1, f2 F2) fun
|
||||
}
|
||||
}
|
||||
|
||||
// Push2 creates a [Tuple3] from a [Tuple2] by appending a constant value
|
||||
func Push2[T1, T2, T3 any](value T3) func(Tuple2[T1, T2]) Tuple3[T1, T2, T3] {
|
||||
return func(t Tuple2[T1, T2]) Tuple3[T1, T2, T3] {
|
||||
return MakeTuple3(t.F1, t.F2, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple3 is a function that converts its 3 parameters into a [Tuple3]
|
||||
func MakeTuple3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3) Tuple3[T1, T2, T3] {
|
||||
return Tuple3[T1, T2, T3]{t1, t2, t3}
|
||||
@@ -436,6 +450,13 @@ func FromArray3[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, T1, T2, T3, R an
|
||||
}
|
||||
}
|
||||
|
||||
// Push3 creates a [Tuple4] from a [Tuple3] by appending a constant value
|
||||
func Push3[T1, T2, T3, T4 any](value T4) func(Tuple3[T1, T2, T3]) Tuple4[T1, T2, T3, T4] {
|
||||
return func(t Tuple3[T1, T2, T3]) Tuple4[T1, T2, T3, T4] {
|
||||
return MakeTuple4(t.F1, t.F2, t.F3, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple4 is a function that converts its 4 parameters into a [Tuple4]
|
||||
func MakeTuple4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4) Tuple4[T1, T2, T3, T4] {
|
||||
return Tuple4[T1, T2, T3, T4]{t1, t2, t3, t4}
|
||||
@@ -568,6 +589,13 @@ func FromArray4[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
|
||||
}
|
||||
}
|
||||
|
||||
// Push4 creates a [Tuple5] from a [Tuple4] by appending a constant value
|
||||
func Push4[T1, T2, T3, T4, T5 any](value T5) func(Tuple4[T1, T2, T3, T4]) Tuple5[T1, T2, T3, T4, T5] {
|
||||
return func(t Tuple4[T1, T2, T3, T4]) Tuple5[T1, T2, T3, T4, T5] {
|
||||
return MakeTuple5(t.F1, t.F2, t.F3, t.F4, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple5 is a function that converts its 5 parameters into a [Tuple5]
|
||||
func MakeTuple5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Tuple5[T1, T2, T3, T4, T5] {
|
||||
return Tuple5[T1, T2, T3, T4, T5]{t1, t2, t3, t4, t5}
|
||||
@@ -711,6 +739,13 @@ func FromArray5[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
|
||||
}
|
||||
}
|
||||
|
||||
// Push5 creates a [Tuple6] from a [Tuple5] by appending a constant value
|
||||
func Push5[T1, T2, T3, T4, T5, T6 any](value T6) func(Tuple5[T1, T2, T3, T4, T5]) Tuple6[T1, T2, T3, T4, T5, T6] {
|
||||
return func(t Tuple5[T1, T2, T3, T4, T5]) Tuple6[T1, T2, T3, T4, T5, T6] {
|
||||
return MakeTuple6(t.F1, t.F2, t.F3, t.F4, t.F5, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple6 is a function that converts its 6 parameters into a [Tuple6]
|
||||
func MakeTuple6[T1, T2, T3, T4, T5, T6 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Tuple6[T1, T2, T3, T4, T5, T6] {
|
||||
return Tuple6[T1, T2, T3, T4, T5, T6]{t1, t2, t3, t4, t5, t6}
|
||||
@@ -865,6 +900,13 @@ func FromArray6[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
|
||||
}
|
||||
}
|
||||
|
||||
// Push6 creates a [Tuple7] from a [Tuple6] by appending a constant value
|
||||
func Push6[T1, T2, T3, T4, T5, T6, T7 any](value T7) func(Tuple6[T1, T2, T3, T4, T5, T6]) Tuple7[T1, T2, T3, T4, T5, T6, T7] {
|
||||
return func(t Tuple6[T1, T2, T3, T4, T5, T6]) Tuple7[T1, T2, T3, T4, T5, T6, T7] {
|
||||
return MakeTuple7(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple7 is a function that converts its 7 parameters into a [Tuple7]
|
||||
func MakeTuple7[T1, T2, T3, T4, T5, T6, T7 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Tuple7[T1, T2, T3, T4, T5, T6, T7] {
|
||||
return Tuple7[T1, T2, T3, T4, T5, T6, T7]{t1, t2, t3, t4, t5, t6, t7}
|
||||
@@ -1030,6 +1072,13 @@ func FromArray7[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
|
||||
}
|
||||
}
|
||||
|
||||
// Push7 creates a [Tuple8] from a [Tuple7] by appending a constant value
|
||||
func Push7[T1, T2, T3, T4, T5, T6, T7, T8 any](value T8) func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||
return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||
return MakeTuple8(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple8 is a function that converts its 8 parameters into a [Tuple8]
|
||||
func MakeTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||
return Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]{t1, t2, t3, t4, t5, t6, t7, t8}
|
||||
@@ -1206,6 +1255,13 @@ func FromArray8[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
|
||||
}
|
||||
}
|
||||
|
||||
// Push8 creates a [Tuple9] from a [Tuple8] by appending a constant value
|
||||
func Push8[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](value T9) func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||
return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||
return MakeTuple9(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple9 is a function that converts its 9 parameters into a [Tuple9]
|
||||
func MakeTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||
return Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]{t1, t2, t3, t4, t5, t6, t7, t8, t9}
|
||||
@@ -1393,6 +1449,13 @@ func FromArray9[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
|
||||
}
|
||||
}
|
||||
|
||||
// Push9 creates a [Tuple10] from a [Tuple9] by appending a constant value
|
||||
func Push9[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](value T10) func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] {
|
||||
return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] {
|
||||
return MakeTuple10(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, value)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTuple10 is a function that converts its 10 parameters into a [Tuple10]
|
||||
func MakeTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] {
|
||||
return Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10}
|
||||
|
@@ -17,6 +17,10 @@
|
||||
// consider to use arrays for simplicity
|
||||
package tuple
|
||||
|
||||
func Of[T1 any](t T1) Tuple1[T1] {
|
||||
return MakeTuple1(t)
|
||||
}
|
||||
|
||||
func First[T1, T2 any](t Tuple2[T1, T2]) T1 {
|
||||
return t.F1
|
||||
}
|
||||
@@ -29,7 +33,7 @@ func Swap[T1, T2 any](t Tuple2[T1, T2]) Tuple2[T2, T1] {
|
||||
return MakeTuple2(t.F2, t.F1)
|
||||
}
|
||||
|
||||
func Of[T1, T2 any](e T2) func(T1) Tuple2[T1, T2] {
|
||||
func Of2[T1, T2 any](e T2) func(T1) Tuple2[T1, T2] {
|
||||
return func(t T1) Tuple2[T1, T2] {
|
||||
return MakeTuple2(t, e)
|
||||
}
|
||||
|
Reference in New Issue
Block a user