mirror of
https://github.com/IBM/fp-go.git
synced 2025-09-01 19:56:12 +02:00
Compare commits
34 Commits
cleue-add-
...
v1.0.24
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
0064ac1c75 | ||
|
8944a66c18 | ||
|
bd0c42db01 | ||
|
e9f03e2d26 | ||
|
bb630810fc | ||
|
9ba9eaacbe | ||
|
a9f6839acd |
@@ -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)
|
||||
}
|
||||
@@ -226,3 +238,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)
|
||||
}
|
||||
|
@@ -388,7 +388,7 @@ func generateApplyHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -266,7 +266,7 @@ func generateBindHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n", pkg)
|
||||
|
||||
|
@@ -150,7 +150,7 @@ func generateEitherHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -27,5 +27,5 @@ func writePackage(f *os.File, pkg string) {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ func generateIdentityHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -81,7 +81,7 @@ func generateIOHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -217,7 +217,7 @@ func generateIOEitherHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -81,7 +81,7 @@ func generateIOOptionHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -148,7 +148,7 @@ func generateOptionHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -337,7 +337,7 @@ func generatePipeHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n", pkg)
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
@@ -117,7 +118,7 @@ func generateReaderHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
@@ -169,7 +169,7 @@ func generateReaderIOEitherHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
|
52
cli/tuple.go
52
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++ {
|
||||
@@ -355,7 +399,7 @@ func generateTupleHelpers(filename string, count int) error {
|
||||
// some header
|
||||
fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.")
|
||||
fmt.Fprintln(f, "// This file was generated by robots at")
|
||||
fmt.Fprintf(f, "// %s\n", time.Now())
|
||||
fmt.Fprintf(f, "// %s\n\n", time.Now())
|
||||
|
||||
fmt.Fprintf(f, "package %s\n\n", pkg)
|
||||
|
||||
@@ -398,6 +442,10 @@ import (
|
||||
generateToArray(f, i)
|
||||
// generate fromArray
|
||||
generateFromArray(f, i)
|
||||
// generate push
|
||||
if i < count {
|
||||
generatePush(f, i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -1,23 +1,8 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioeither
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:20.012425 +0200 CEST m=+0.019517901
|
||||
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@@ -1,23 +1,8 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package generic
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:20.012425 +0200 CEST m=+0.019517901
|
||||
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package option defines the [Either] datastructure and its monadic operations
|
||||
package either
|
||||
|
||||
//go:generate go run .. either --count 10 --filename gen.go
|
||||
|
@@ -146,8 +146,8 @@ func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] {
|
||||
return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A])
|
||||
}
|
||||
|
||||
func ToOption[E, A any]() func(Either[E, A]) O.Option[A] {
|
||||
return Fold(F.Ignore1of1[E](O.None[A]), O.Some[A])
|
||||
func ToOption[E, A any](ma Either[E, A]) O.Option[A] {
|
||||
return MonadFold(ma, F.Ignore1of1[E](O.None[A]), O.Some[A])
|
||||
}
|
||||
|
||||
func FromError[A any](f func(a A) error) func(A) Either[error, A] {
|
||||
@@ -182,7 +182,7 @@ func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Eithe
|
||||
}
|
||||
}
|
||||
|
||||
func FromNillable[E, A any](e E) func(*A) Either[E, *A] {
|
||||
func FromNillable[A, E any](e E) func(*A) Either[E, *A] {
|
||||
return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e))
|
||||
}
|
||||
|
||||
|
58
either/examples_create_test.go
Normal file
58
either/examples_create_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/IBM/fp-go/errors"
|
||||
)
|
||||
|
||||
func ExampleEither_creation() {
|
||||
// Build an Either
|
||||
leftValue := Left[string](fmt.Errorf("some error"))
|
||||
rightValue := Right[error]("value")
|
||||
|
||||
// Build from a value
|
||||
fromNillable := FromNillable[string](fmt.Errorf("value was nil"))
|
||||
leftFromNil := fromNillable(nil)
|
||||
value := "value"
|
||||
rightFromPointer := fromNillable(&value)
|
||||
|
||||
// some predicate
|
||||
isEven := func(num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
fromEven := FromPredicate(isEven, errors.OnSome[int]("%d is an odd number"))
|
||||
leftFromPred := fromEven(3)
|
||||
rightFromPred := fromEven(4)
|
||||
|
||||
fmt.Println(leftValue)
|
||||
fmt.Println(rightValue)
|
||||
fmt.Println(leftFromNil)
|
||||
fmt.Println(IsRight(rightFromPointer))
|
||||
fmt.Println(leftFromPred)
|
||||
fmt.Println(rightFromPred)
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, string](some error)
|
||||
// Right[<nil>, string](value)
|
||||
// Left[*errors.errorString, *string](value was nil)
|
||||
// true
|
||||
// Left[*errors.errorString, int](3 is an odd number)
|
||||
// Right[<nil>, int](4)
|
||||
|
||||
}
|
64
either/examples_extract_test.go
Normal file
64
either/examples_extract_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
)
|
||||
|
||||
func ExampleEither_extraction() {
|
||||
leftValue := Left[int](fmt.Errorf("Division by Zero!"))
|
||||
rightValue := Right[error](10)
|
||||
|
||||
// Convert Either[E, A] to A with a default value
|
||||
leftWithDefault := GetOrElse(F.Constant1[error](0))(leftValue) // 0
|
||||
rightWithDefault := GetOrElse(F.Constant1[error](0))(rightValue) // 10
|
||||
|
||||
// Apply a different function on Left(...)/Right(...)
|
||||
doubleOrZero := Fold(F.Constant1[error](0), N.Mul(2)) // func(Either[error, int]) int
|
||||
doubleFromLeft := doubleOrZero(leftValue) // 0
|
||||
doubleFromRight := doubleOrZero(rightValue) // 20
|
||||
|
||||
// Pro-tip: Fold is short for the following:
|
||||
doubleOrZeroBis := F.Flow2(
|
||||
Map[error](N.Mul(2)),
|
||||
GetOrElse(F.Constant1[error](0)),
|
||||
)
|
||||
doubleFromLeftBis := doubleOrZeroBis(leftValue) // 0
|
||||
doubleFromRightBis := doubleOrZeroBis(rightValue) // 20
|
||||
|
||||
fmt.Println(leftValue)
|
||||
fmt.Println(rightValue)
|
||||
fmt.Println(leftWithDefault)
|
||||
fmt.Println(rightWithDefault)
|
||||
fmt.Println(doubleFromLeft)
|
||||
fmt.Println(doubleFromRight)
|
||||
fmt.Println(doubleFromLeftBis)
|
||||
fmt.Println(doubleFromRightBis)
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString, int](Division by Zero!)
|
||||
// Right[<nil>, int](10)
|
||||
// 0
|
||||
// 10
|
||||
// 0
|
||||
// 20
|
||||
// 0
|
||||
// 20
|
||||
}
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:23.165015 +0200 CEST m=+0.038998801
|
||||
// 2023-08-17 22:58:57.9925506 +0200 CEST m=+0.033401101
|
||||
|
||||
package either
|
||||
|
||||
import (
|
||||
|
@@ -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))))
|
||||
|
@@ -28,6 +28,22 @@ func OnNone(msg string, args ...any) func() error {
|
||||
}
|
||||
}
|
||||
|
||||
// OnSome generates a unary function that produces a formatted error
|
||||
func OnSome[T any](msg string, args ...any) func(T) error {
|
||||
l := len(args)
|
||||
if l == 0 {
|
||||
return func(value T) error {
|
||||
return fmt.Errorf(msg, value)
|
||||
}
|
||||
}
|
||||
return func(value T) error {
|
||||
data := make([]any, l)
|
||||
copy(data[1:], args)
|
||||
data[0] = value
|
||||
return fmt.Errorf(msg, data...)
|
||||
}
|
||||
}
|
||||
|
||||
// OnError generates a unary function that produces a formatted error. The argument
|
||||
// to that function is the root cause of the error and the message will be augmented with
|
||||
// a format string containing %w
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:40.161663 +0200 CEST m=+0.009458101
|
||||
// 2023-08-17 22:59:01.9404821 +0200 CEST m=+0.161366801
|
||||
|
||||
package function
|
||||
|
||||
// Combinations for a total of 1 arguments
|
||||
|
30
function/cache.go
Normal file
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"
|
||||
)
|
||||
|
||||
// Cache converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func Cache[K comparable, T any](f func(K) T) func(K) T {
|
||||
return G.Cache(f)
|
||||
}
|
||||
|
||||
// ContramapCache converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func ContramapCache[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T {
|
||||
return G.ContramapCache[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 := Cache(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)
|
||||
}
|
@@ -13,6 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package function implements function composition primitives, most prominently [Pipe2] and [Flow2]
|
||||
package function
|
||||
|
||||
//go:generate go run .. pipe --count 20 --filename gen.go
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:27.8029852 +0200 CEST m=+0.051432001
|
||||
// 2023-08-17 22:58:59.8663985 +0200 CEST m=+0.103744101
|
||||
|
||||
package function
|
||||
|
||||
// Pipe0 takes an initial value t0 and successively applies 0 functions where the input of a function is the return value of the previous function
|
||||
|
65
function/generic/cache.go
Normal file
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"
|
||||
)
|
||||
|
||||
// Cache converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func Cache[F ~func(K) T, K comparable, T any](f F) F {
|
||||
return ContramapCache[F](func(k K) K { return k })(f)
|
||||
}
|
||||
|
||||
// ContramapCache converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func ContramapCache[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
|
||||
return CacheCallback[F](kf, getOrCreate[K, T]())
|
||||
}
|
||||
|
||||
// getOrCreate is a naive implementation of a cache, without bounds
|
||||
func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
|
||||
cache := make(map[K]func() T)
|
||||
var l sync.Mutex
|
||||
|
||||
return func(k K, cb func() func() T) func() T {
|
||||
// only lock to access a lazy accessor to the value
|
||||
l.Lock()
|
||||
existing, ok := cache[k]
|
||||
if !ok {
|
||||
existing = cb()
|
||||
cache[k] = existing
|
||||
}
|
||||
l.Unlock()
|
||||
// compute the value outside of the lock
|
||||
return existing
|
||||
}
|
||||
}
|
||||
|
||||
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
|
||||
func CacheCallback[F ~func(A) T, KF func(A) K, C ~func(K, func() func() T) func() T, A any, K comparable, T any](kf KF, getOrCreate C) func(F) F {
|
||||
return func(f F) F {
|
||||
return func(a A) T {
|
||||
// cache entry
|
||||
return getOrCreate(kf(a), func() func() T {
|
||||
return L.Memoize[func() T](func() T {
|
||||
return f(a)
|
||||
})
|
||||
})()
|
||||
}
|
||||
}
|
||||
}
|
62
function/pipe_test.go
Normal file
62
function/pipe_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func addSthg(value int) int {
|
||||
return value + 1
|
||||
}
|
||||
|
||||
func doSthgElse(value int) int {
|
||||
return value * 2
|
||||
}
|
||||
|
||||
func doFinalSthg(value int) string {
|
||||
return fmt.Sprintf("final value: %d", value)
|
||||
}
|
||||
|
||||
func Example() {
|
||||
// start point
|
||||
value := 1
|
||||
// imperative style
|
||||
value1 := addSthg(value) // 2
|
||||
value2 := doSthgElse(value1) // 4
|
||||
finalValueImperative := doFinalSthg(value2) // "final value: 4"
|
||||
|
||||
// the same but inline
|
||||
finalValueInline := doFinalSthg(doSthgElse(addSthg(value)))
|
||||
|
||||
// with pipe
|
||||
finalValuePipe := Pipe3(value, addSthg, doSthgElse, doFinalSthg)
|
||||
|
||||
// with flow
|
||||
transform := Flow3(addSthg, doSthgElse, doFinalSthg)
|
||||
finalValueFlow := transform(value)
|
||||
|
||||
fmt.Println(finalValueImperative)
|
||||
fmt.Println(finalValueInline)
|
||||
fmt.Println(finalValuePipe)
|
||||
fmt.Println(finalValueFlow)
|
||||
|
||||
// Output:
|
||||
// final value: 4
|
||||
// final value: 4
|
||||
// final value: 4
|
||||
// final value: 4
|
||||
}
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:46.7920356 +0200 CEST m=+0.045177201
|
||||
// 2023-08-17 22:59:06.2987136 +0200 CEST m=+0.049156901
|
||||
|
||||
package identity
|
||||
|
||||
import (
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:49.4210571 +0200 CEST m=+0.040316501
|
||||
// 2023-08-17 22:59:08.1313362 +0200 CEST m=+0.111171801
|
||||
|
||||
package apply
|
||||
|
||||
import (
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
18
io/gen.go
18
io/gen.go
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:57.7378739 +0200 CEST m=+0.063371401
|
||||
// 2023-08-17 22:59:10.1040409 +0200 CEST m=+0.089470201
|
||||
|
||||
package io
|
||||
|
||||
import (
|
||||
|
@@ -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,21 +1,6 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:48:57.7499229 +0200 CEST m=+0.075420401
|
||||
// 2023-08-17 22:59:10.1132645 +0200 CEST m=+0.098693801
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
@@ -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,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:00.7618943 +0200 CEST m=+0.065441701
|
||||
// 2023-08-17 22:59:12.0033119 +0200 CEST m=+0.096740401
|
||||
|
||||
package ioeither
|
||||
|
||||
import (
|
||||
|
79
ioeither/generic/bind.go
Normal file
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,21 +1,6 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:00.7765028 +0200 CEST m=+0.080050201
|
||||
// 2023-08-17 22:59:12.0246635 +0200 CEST m=+0.118092001
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:03.8471425 +0200 CEST m=+0.087124201
|
||||
// 2023-08-17 22:59:14.0582736 +0200 CEST m=+0.248503201
|
||||
|
||||
package iooption
|
||||
|
||||
import (
|
||||
|
@@ -1,21 +1,6 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:03.8541587 +0200 CEST m=+0.094140401
|
||||
// 2023-08-17 22:59:14.0642289 +0200 CEST m=+0.254458501
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
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,12 +24,33 @@ type Number interface {
|
||||
}
|
||||
|
||||
// Add is a curried function used to add two numbers
|
||||
func Add[T Number](left T) func(T) T {
|
||||
return func(right T) T {
|
||||
func Add[T Number](right T) func(T) T {
|
||||
return func(left T) T {
|
||||
return left + right
|
||||
}
|
||||
}
|
||||
|
||||
// Sub is a curried function used to subtract two numbers
|
||||
func Sub[T Number](right T) func(T) T {
|
||||
return func(left T) T {
|
||||
return left - right
|
||||
}
|
||||
}
|
||||
|
||||
// Mul is a curried function used to multiply two numbers
|
||||
func Mul[T Number](right T) func(T) T {
|
||||
return func(left T) T {
|
||||
return left * right
|
||||
}
|
||||
}
|
||||
|
||||
// Div is a curried function used to divide two numbers
|
||||
func Div[T Number](right T) func(T) T {
|
||||
return func(left T) T {
|
||||
return left / right
|
||||
}
|
||||
}
|
||||
|
||||
// Inc is a function that increments a number
|
||||
func Inc[T Number](value T) T {
|
||||
return value + 1
|
||||
|
@@ -13,6 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package option defines the [Option] datastructure and its monadic operations
|
||||
package option
|
||||
|
||||
//go:generate go run .. option --count 10 --filename gen.go
|
||||
|
55
option/examples_create_test.go
Normal file
55
option/examples_create_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package option
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleOption_creation() {
|
||||
|
||||
// Build an Option
|
||||
none1 := None[int]()
|
||||
some1 := Some("value")
|
||||
|
||||
// Build from a value
|
||||
fromNillable := FromNillable[string]
|
||||
nonFromNil := fromNillable(nil) // None[*string]
|
||||
value := "value"
|
||||
someFromPointer := fromNillable(&value) // Some[*string](xxx)
|
||||
|
||||
// some predicate
|
||||
isEven := func(num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
|
||||
fromEven := FromPredicate(isEven)
|
||||
noneFromPred := fromEven(3) // None[int]
|
||||
someFromPred := fromEven(4) // Some[int](4)
|
||||
|
||||
fmt.Println(none1)
|
||||
fmt.Println(some1)
|
||||
fmt.Println(nonFromNil)
|
||||
fmt.Println(IsSome(someFromPointer))
|
||||
fmt.Println(noneFromPred)
|
||||
fmt.Println(someFromPred)
|
||||
|
||||
// Output:
|
||||
// None[int]
|
||||
// Some[string](value)
|
||||
// None[*string]
|
||||
// true
|
||||
// None[int]
|
||||
// Some[int](4)
|
||||
}
|
73
option/examples_extract_test.go
Normal file
73
option/examples_extract_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package option
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
)
|
||||
|
||||
func ExampleOption_extraction() {
|
||||
|
||||
noneValue := None[int]()
|
||||
someValue := Of(42)
|
||||
|
||||
// Convert Option[T] to T
|
||||
fromNone, okFromNone := Unwrap(noneValue) // 0, false
|
||||
fromSome, okFromSome := Unwrap(someValue) // 42, true
|
||||
|
||||
// Convert Option[T] with a default value
|
||||
noneWithDefault := GetOrElse(F.Constant(0))(noneValue) // 0
|
||||
someWithDefault := GetOrElse(F.Constant(0))(someValue) // 42
|
||||
|
||||
// Apply a different function on None/Some(...)
|
||||
doubleOrZero := Fold(
|
||||
F.Constant(0), // none case
|
||||
N.Mul(2), // some case
|
||||
) // func(ma Option[int]) int
|
||||
|
||||
doubleFromNone := doubleOrZero(noneValue) // 0
|
||||
doubleFromSome := doubleOrZero(someValue) // 84
|
||||
|
||||
// Pro-tip: Fold is short for the following:
|
||||
doubleOfZeroBis := F.Flow2(
|
||||
Map(N.Mul(2)), // some case
|
||||
GetOrElse(F.Constant(0)), // none case
|
||||
)
|
||||
doubleFromNoneBis := doubleOfZeroBis(noneValue) // 0
|
||||
doubleFromSomeBis := doubleOfZeroBis(someValue) // 84
|
||||
|
||||
fmt.Printf("%d, %t\n", fromNone, okFromNone)
|
||||
fmt.Printf("%d, %t\n", fromSome, okFromSome)
|
||||
fmt.Println(noneWithDefault)
|
||||
fmt.Println(someWithDefault)
|
||||
fmt.Println(doubleFromNone)
|
||||
fmt.Println(doubleFromSome)
|
||||
fmt.Println(doubleFromNoneBis)
|
||||
fmt.Println(doubleFromSomeBis)
|
||||
|
||||
// Output:
|
||||
// 0, false
|
||||
// 42, true
|
||||
// 0
|
||||
// 42
|
||||
// 0
|
||||
// 84
|
||||
// 0
|
||||
// 84
|
||||
}
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:06.6236747 +0200 CEST m=+0.039283301
|
||||
// 2023-08-17 22:59:16.3450991 +0200 CEST m=+0.104894201
|
||||
|
||||
package option
|
||||
|
||||
import (
|
||||
|
38
option/ord.go
Normal file
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")))
|
||||
|
||||
}
|
@@ -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))
|
||||
}
|
||||
}
|
||||
|
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,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:14.4173227 +0200 CEST m=+0.013163201
|
||||
// 2023-08-17 22:59:18.4448291 +0200 CEST m=+0.161369001
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
|
@@ -1,21 +1,6 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:14.4173227 +0200 CEST m=+0.013163201
|
||||
// 2023-08-17 22:59:18.5047315 +0200 CEST m=+0.221271401
|
||||
package generic
|
||||
|
||||
// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [GRA]
|
||||
|
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:16.5272334 +0200 CEST m=+0.043108701
|
||||
// 2023-08-17 22:59:21.3704716 +0200 CEST m=+0.110126101
|
||||
|
||||
package readerioeither
|
||||
|
||||
import (
|
||||
|
@@ -1,21 +1,6 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:16.5292332 +0200 CEST m=+0.045108501
|
||||
// 2023-08-17 22:59:21.3811525 +0200 CEST m=+0.120807001
|
||||
package generic
|
||||
|
||||
import (
|
||||
|
@@ -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) {
|
||||
@@ -109,7 +109,7 @@ func ExampleFoldMap() {
|
||||
"c": "c",
|
||||
}
|
||||
|
||||
fold := FoldMap[string, string](S.Monoid)(strings.ToUpper)
|
||||
fold := FoldMapOrd[string, string](S.Ord)(S.Monoid)(strings.ToUpper)
|
||||
|
||||
fmt.Println(fold(src))
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package record
|
||||
|
||||
import (
|
||||
|
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
|
||||
}
|
||||
|
81
tuple/gen.go
81
tuple/gen.go
@@ -1,21 +1,7 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2023-07-28 22:49:18.7141124 +0200 CEST m=+0.109464201
|
||||
// 2023-08-17 22:59:23.6923568 +0200 CEST m=+0.056359401
|
||||
|
||||
package tuple
|
||||
|
||||
import (
|
||||
@@ -219,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}
|
||||
@@ -329,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}
|
||||
@@ -450,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}
|
||||
@@ -582,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}
|
||||
@@ -725,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}
|
||||
@@ -879,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}
|
||||
@@ -1044,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}
|
||||
@@ -1220,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}
|
||||
@@ -1407,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}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user