1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-28 19:49:07 +02:00

Compare commits

...

12 Commits

Author SHA1 Message Date
Dr. Carsten Leue
fb3b1f115c fix: add examples for [Frisby's Mostly Adequate Guide] 2023-09-01 17:31:47 +02:00
Carsten Leue
ce66cf2295 Merge pull request #31 from IBM/cleue-add-cache
fix: implement simple cache for pure functions
2023-08-31 11:07:46 +02:00
Dr. Carsten Leue
80e579dd0b fix: implement simple cache for pure functions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-31 10:27:32 +02:00
Carsten Leue
ddafd1ee57 Merge pull request #30 from IBM/cleue-sample-ioeither
Add samples for IOEither
2023-08-29 09:16:17 +02:00
Dr. Carsten Leue
b5f077da71 fix: remove types from this branch
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-29 09:11:03 +02:00
Dr. Carsten Leue
1a0c40b419 fix: add helpers for reflect types
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-08-25 22:53:09 +02:00
Carsten Leue
d5d89b1853 fix: add runtime type validation
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-24 22:49:03 +02:00
Carsten Leue
0f061a5099 fix: initial implementation of types
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-22 22:34:05 +02:00
Carsten Leue
45e05f25ff Merge pull request #27 from jdbaldry/main-1
Fix a couple of spelling errors
2023-08-21 14:49:54 +02:00
Carsten Leue
a390d53451 add: bind to IOEither
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-18 22:15:26 +02:00
Jack Baldry
1346b9378a Fix a couple spelling errors 2023-08-18 09:45:27 +01:00
Carsten Leue
befd4f471e fix: add basic bind as do notation
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-08-17 23:03:03 +02:00
57 changed files with 1395 additions and 55 deletions

View File

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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -20,6 +20,7 @@ import (
"log"
"os"
"path/filepath"
"strings"
"time"
C "github.com/urfave/cli/v2"
@@ -36,8 +37,51 @@ func writeTupleType(f *os.File, symbol string, i int) {
fmt.Fprintf(f, "]")
}
func makeTupleType(name string) func(i int) string {
return func(i int) string {
var buf strings.Builder
buf.WriteString(fmt.Sprintf("Tuple%d[", i))
for j := 0; j < i; j++ {
if j > 0 {
buf.WriteString(", ")
}
buf.WriteString(fmt.Sprintf("%s%d", name, j+1))
}
buf.WriteString("]")
return buf.String()
}
}
func generatePush(f *os.File, i int) {
tuple1 := makeTupleType("T")(i)
tuple2 := makeTupleType("T")(i + 1)
// Create the replicate version
fmt.Fprintf(f, "\n// Push%d creates a [Tuple%d] from a [Tuple%d] by appending a constant value\n", i, i+1, i)
fmt.Fprintf(f, "func Push%d[", i)
// function prototypes
for j := 0; j <= i; j++ {
if j > 0 {
fmt.Fprintf(f, ", ")
}
fmt.Fprintf(f, "T%d", j+1)
}
fmt.Fprintf(f, " any](value T%d) func(%s) %s {\n", i+1, tuple1, tuple2)
fmt.Fprintf(f, " return func(t %s) %s {\n", tuple1, tuple2)
fmt.Fprintf(f, " return MakeTuple%d(", i+1)
for j := 0; j < i; j++ {
if j > 0 {
fmt.Fprintf(f, ", ")
}
fmt.Fprintf(f, "t.F%d", j+1)
}
fmt.Fprintf(f, ", value)\n")
fmt.Fprintf(f, " }\n")
fmt.Fprintf(f, "}\n")
}
func generateReplicate(f *os.File, i int) {
// Create the optionize version
// Create the replicate version
fmt.Fprintf(f, "\n// Replicate%d creates a [Tuple%d] with all fields set to the input value `t`\n", i, i)
fmt.Fprintf(f, "func Replicate%d[T any](t T) Tuple%d[", i, i)
for j := 1; j <= i; j++ {
@@ -398,6 +442,10 @@ import (
generateToArray(f, i)
// generate fromArray
generateFromArray(f, i)
// generate push
if i < count {
generatePush(f, i)
}
}
return nil

View File

@@ -2,7 +2,7 @@ package readerioeither
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:37:34.0836986 +0200 CEST m=+0.009764601
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
import (
"context"

View File

@@ -2,7 +2,7 @@ package generic
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:37:34.0838811 +0200 CEST m=+0.009947101
// 2023-08-17 22:58:56.457404 +0200 CEST m=+0.024265101
import (
"context"

View File

@@ -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] {

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:37:35.9608486 +0200 CEST m=+0.009664501
// 2023-08-17 22:58:57.9925506 +0200 CEST m=+0.033401101
package either

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:37:50.171038 +0200 CEST m=+0.031513801
// 2023-08-17 22:59:01.9404821 +0200 CEST m=+0.161366801
package function

30
function/cache.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package function
import (
G "github.com/IBM/fp-go/function/generic"
)
// 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
View File

@@ -0,0 +1,50 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package function
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCache(t *testing.T) {
var count int
withSideEffect := func(n int) int {
count++
return n
}
cached := 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)
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:37:38.4916948 +0200 CEST m=+0.060448601
// 2023-08-17 22:58:59.8663985 +0200 CEST m=+0.103744101
package function

65
function/generic/cache.go Normal file
View File

@@ -0,0 +1,65 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
"sync"
L "github.com/IBM/fp-go/internal/lazy"
)
// 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)
})
})()
}
}
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:00.2678592 +0200 CEST m=+0.008890401
// 2023-08-17 22:59:06.2987136 +0200 CEST m=+0.049156901
package identity

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:01.6948567 +0200 CEST m=+0.013530301
// 2023-08-17 22:59:08.1313362 +0200 CEST m=+0.111171801
package apply

View File

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

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

@@ -0,0 +1,75 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bindt
import (
F "github.com/IBM/fp-go/function"
I "github.com/IBM/fp-go/identity"
T "github.com/IBM/fp-go/tuple"
)
func Bind[SET ~func(B) func(S1) S2, FCT ~func(S1) HKTB, S1, S2, B, HKTS1, HKTS2, HKTB any](
mchain func(func(S1) HKTS2) func(HKTS1) HKTS2,
mmap func(func(B) S2) func(HKTB) HKTS2,
s SET,
f FCT,
) func(HKTS1) HKTS2 {
return mchain(F.Flow3(
T.Replicate2[S1],
T.Map2(F.Flow2(
I.Ap[S2, S1],
F.Flow2(
F.Bind1st(F.Flow2[SET, func(func(S1) S2) S2], s),
mmap,
)), f),
T.Tupled2(I.MonadAp[HKTS2, HKTB]),
))
}
func BindTo[SET ~func(B) S2, S2, B, HKTS2, HKTB any](
mmap func(func(B) S2) func(HKTB) HKTS2,
s SET,
) func(HKTB) HKTS2 {
return mmap(s)
}
func ApS[
SET ~func(B) func(S1) S2,
S1, S2, B, HKTS1S2, HKTS1, HKTS2, HKTB any,
](
ap func(HKTS1) func(HKTS1S2) HKTS2,
mmap func(func(B) func(S1) S2) func(HKTB) HKTS1S2,
s SET, fb HKTB) func(HKTS1) HKTS2 {
return F.Flow2(
ap,
I.Ap[HKTS2, HKTS1S2](mmap(s)(fb)),
)
}
func Let[SET ~func(B) func(S1) S2, FCT ~func(S1) B, S1, S2, B, HKTS1, HKTS2 any](
mmap func(func(S1) S2) func(HKTS1) HKTS2,
s SET,
f FCT,
) func(HKTS1) HKTS2 {
return mmap(F.Flow3(
T.Replicate2[S1],
T.Map2(F.Flow2(
I.Ap[S2, S1],
F.Bind1st(F.Flow2[SET, func(func(S1) S2) S2], s)), f),
T.Tupled2(I.MonadAp[S2, B]),
))
}

34
internal/lazy/memoize.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lazy
import "sync"
// Memoize computes the value of the provided IO monad lazily but exactly once
func Memoize[GA ~func() A, A any](ma GA) GA {
// synchronization primitives
var once sync.Once
var result A
// callback
gen := func() {
result = ma()
}
// returns our memoized wrapper
return func() A {
once.Do(gen)
return result
}
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:07.3305497 +0200 CEST m=+0.014821301
// 2023-08-17 22:59:10.1040409 +0200 CEST m=+0.089470201
package io

View File

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

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:07.3305497 +0200 CEST m=+0.014821301
// 2023-08-17 22:59:10.1132645 +0200 CEST m=+0.098693801
package generic
import (

View File

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

44
ioeither/bind.go Normal file
View File

@@ -0,0 +1,44 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ioeither
import (
G "github.com/IBM/fp-go/ioeither/generic"
)
// Bind applies a function to an input state and merges the result into that state
func Bind[E, A, S1, S2 any](s func(A) func(S1) S2, f func(S1) IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], func(S1) IOEither[E, A]](s, f)
}
// BindTo initializes some state based on a value
func BindTo[
E, A, S2 any](s func(A) S2) func(IOEither[E, A]) IOEither[E, S2] {
return G.BindTo[IOEither[E, S2], IOEither[E, A]](s)
}
func ApS[
E, A, S1, S2 any,
](s func(A) func(S1) S2, fa IOEither[E, A]) func(IOEither[E, S1]) IOEither[E, S2] {
return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, A], IOEither[E, func(S1) S2]](s, fa)
}
func Let[E, A, S1, S2 any](
s func(A) func(S1) S2,
f func(S1) A,
) func(IOEither[E, S1]) IOEither[E, S2] {
return G.Let[IOEither[E, S1], IOEither[E, S2]](s, f)
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ioeither
import (
"fmt"
E "github.com/IBM/fp-go/either"
)
func ExampleIOEither_creation() {
// Build an IOEither
leftValue := Left[string](fmt.Errorf("some error"))
rightValue := Right[error]("value")
// Convert from Either
eitherValue := E.Of[error](42)
ioFromEither := FromEither(eitherValue)
// some predicate
isEven := func(num int) (int, error) {
if num%2 == 0 {
return num, nil
}
return 0, fmt.Errorf("%d is an odd number", num)
}
fromEven := Eitherize1(isEven)
leftFromPred := fromEven(3)
rightFromPred := fromEven(4)
fmt.Println(leftValue())
fmt.Println(rightValue())
fmt.Println(ioFromEither())
fmt.Println(leftFromPred())
fmt.Println(rightFromPred())
// Output:
// Left[*errors.errorString, string](some error)
// Right[<nil>, string](value)
// Right[<nil>, int](42)
// Left[*errors.errorString, int](3 is an odd number)
// Right[<nil>, int](4)
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ioeither
import (
"fmt"
"log"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
T "github.com/IBM/fp-go/tuple"
)
func ExampleIOEither_do() {
foo := Of[error]("foo")
bar := Of[error](1)
// quux consumes the state of three bindings and returns an [IO] instead of an [IOEither]
quux := func(t T.Tuple3[string, int, string]) IO.IO[any] {
return IO.FromImpure(func() {
log.Printf("t1: %s, t2: %d, t3: %s", t.F1, t.F2, t.F3)
})
}
transform := func(t T.Tuple3[string, int, string]) int {
return len(t.F1) + t.F2 + len(t.F3)
}
b := F.Pipe5(
foo,
BindTo[error](T.Of[string]),
ApS(T.Push1[string, int], bar),
Bind(T.Push2[string, int, string], func(t T.Tuple2[string, int]) IOEither[error, string] {
return Of[error](fmt.Sprintf("%s%d", t.F1, t.F2))
}),
ChainFirstIOK[error](quux),
Map[error](transform),
)
fmt.Println(b())
// Output:
// Right[<nil>, int](8)
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ioeither
import (
"fmt"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
IO "github.com/IBM/fp-go/io"
)
func ExampleIOEither_extraction() {
// IOEither
someIOEither := Right[error](42)
eitherValue := someIOEither() // E.Right(42)
value := E.GetOrElse(F.Constant1[error](0))(eitherValue) // 42
// Or more directly
infaillibleIO := GetOrElse(F.Constant1[error](IO.Of(0)))(someIOEither) // => IO.Right(42)
valueFromIO := infaillibleIO() // => 42
fmt.Println(eitherValue)
fmt.Println(value)
fmt.Println(valueFromIO)
// Output:
// Right[<nil>, int](42)
// 42
// 42
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:09.4187123 +0200 CEST m=+0.099620101
// 2023-08-17 22:59:12.0033119 +0200 CEST m=+0.096740401
package ioeither

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

@@ -0,0 +1,79 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
ET "github.com/IBM/fp-go/either"
G "github.com/IBM/fp-go/internal/bindt"
)
// Bind applies a function to an input state and merges the result into that state
func Bind[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
GA ~func() ET.Either[E, A],
FCT ~func(S1) GA,
E any,
SET ~func(A) func(S1) S2,
A, S1, S2 any](s SET, f FCT) func(GS1) GS2 {
return G.Bind(
Chain[GS1, GS2, E, S1, S2],
Map[GA, GS2, E, A, S2],
s,
f,
)
}
// BindTo initializes some state based on a value
func BindTo[
GS2 ~func() ET.Either[E, S2],
GA ~func() ET.Either[E, A],
E any,
SET ~func(A) S2,
A, S2 any](s SET) func(GA) GS2 {
return G.BindTo(
Map[GA, GS2, E, A, S2],
s,
)
}
func ApS[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
GB ~func() ET.Either[E, B],
GS1S2 ~func() ET.Either[E, func(S1) S2],
SET ~func(B) func(S1) S2,
E, S1, S2, B any,
](s SET, fb GB) func(GS1) GS2 {
return G.ApS[SET, S1, S2, B, GS1S2, GS1, GS2, GB](
Ap[GS2, GS1S2, GS1, E, S1, S2],
Map[GB, GS1S2, E, B, func(S1) S2],
s,
fb,
)
}
func Let[
GS1 ~func() ET.Either[E, S1],
GS2 ~func() ET.Either[E, S2],
SET ~func(B) func(S1) S2,
FCT ~func(S1) B,
E, S1, S2, B any](
s SET,
f FCT,
) func(GS1) GS2 {
return G.Let[SET, FCT, S1, S2, B, GS1, GS2](Map[GS1, GS2, E, S1, S2], s, f)
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:09.4489926 +0200 CEST m=+0.129900401
// 2023-08-17 22:59:12.0246635 +0200 CEST m=+0.118092001
package generic
import (

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:12.1021634 +0200 CEST m=+0.041956701
// 2023-08-17 22:59:14.0582736 +0200 CEST m=+0.248503201
package iooption

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:12.1210201 +0200 CEST m=+0.060813401
// 2023-08-17 22:59:14.0642289 +0200 CEST m=+0.254458501
package generic
import (

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic
import (
R "reflect"
F "github.com/IBM/fp-go/function"
LG "github.com/IBM/fp-go/io/generic"
L "github.com/IBM/fp-go/lazy"
N "github.com/IBM/fp-go/number"
I "github.com/IBM/fp-go/number/integer"
O "github.com/IBM/fp-go/option"
T "github.com/IBM/fp-go/tuple"
)
func FromReflect[GR ~func() O.Option[T.Tuple2[GR, R.Value]]](val R.Value) GR {
// recursive callback
var recurse func(idx int) GR
// limits the index
fromPred := O.FromPredicate(I.Between(0, val.Len()))
recurse = func(idx int) GR {
return F.Pipe3(
idx,
L.Of[int],
L.Map(fromPred),
LG.Map[L.Lazy[O.Option[int]], GR](O.Map(
F.Flow2(
T.Replicate2[int],
T.Map2(F.Flow2(N.Add(1), recurse), val.Index),
),
)),
)
}
// start the recursion
return recurse(0)
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package stateless
import (
R "reflect"
G "github.com/IBM/fp-go/iterator/stateless/generic"
)
// FromReflect creates an iterator that can iterate over types that define [R.Index] and [R.Len]
func FromReflect(val R.Value) Iterator[R.Value] {
return G.FromReflect[Iterator[R.Value]](val)
}

View File

@@ -0,0 +1,38 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package stateless
import (
"reflect"
"testing"
A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
"github.com/stretchr/testify/assert"
)
func TestReflect(t *testing.T) {
ar := A.From("a", "b", "c")
res := F.Pipe3(
reflect.ValueOf(ar),
FromReflect,
ToArray[reflect.Value],
A.Map(reflect.Value.String),
)
assert.Equal(t, ar, res)
}

View File

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

38
lazy/example_lazy_test.go Normal file
View File

@@ -0,0 +1,38 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lazy
import (
"fmt"
"strconv"
F "github.com/IBM/fp-go/function"
)
func ExampleLazy_creation() {
// lazy function of a constant value
val := Of(42)
// create another function to transform this
valS := F.Pipe1(
val,
Map(strconv.Itoa),
)
fmt.Println(valS())
// Output:
// 42
}

View File

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

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

@@ -0,0 +1,23 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package integer
import "strconv"
var (
// ToString converts an integer to a string
ToString = strconv.Itoa
)

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:14.9503867 +0200 CEST m=+0.020206701
// 2023-08-17 22:59:16.3450991 +0200 CEST m=+0.104894201
package option

View File

@@ -131,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 {
@@ -142,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 {
@@ -156,7 +152,7 @@ func Leq[A any](O Ord[A]) func(A) func(A) bool {
/**
* Test whether one value is _strictly greater than_ another
*/
func Gt[A any](O Ord[A]) func(A) func(A) bool {
func cc[A any](O Ord[A]) func(A) func(A) bool {
return func(second A) func(A) bool {
return func(first A) bool {
return O.Compare(first, second) > 0
@@ -164,9 +160,7 @@ func Gt[A any](O Ord[A]) func(A) func(A) bool {
}
}
/**
* Test whether one value is greater or equal than_ another
*/
// Geq tests whether one value is greater or equal than_ another
func Geq[A any](O Ord[A]) func(A) func(A) bool {
return func(second A) func(A) bool {
return func(first A) bool {

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:23.1396887 +0200 CEST m=+0.020805301
// 2023-08-17 22:59:18.4448291 +0200 CEST m=+0.161369001
package reader

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:23.1402159 +0200 CEST m=+0.021332501
// 2023-08-17 22:59:18.5047315 +0200 CEST m=+0.221271401
package generic
// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [GRA]

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:26.9546728 +0200 CEST m=+0.032178301
// 2023-08-17 22:59:21.3704716 +0200 CEST m=+0.110126101
package readerioeither

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:26.9556725 +0200 CEST m=+0.033178001
// 2023-08-17 22:59:21.3811525 +0200 CEST m=+0.120807001
package generic
import (

View File

@@ -98,7 +98,7 @@ 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)
}

View File

@@ -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) {

View 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
View 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)
}

View 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.

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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
}

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mostlyadequate
import (
"regexp"
"strings"
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)
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)
}
}
}

View File

@@ -0,0 +1,60 @@
// 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"
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!
}

View File

@@ -0,0 +1,133 @@
// 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"
E "github.com/IBM/fp-go/either"
"github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
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
}
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),
)
)
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
}

View File

@@ -0,0 +1,64 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mostlyadequate
import (
"fmt"
A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
O "github.com/IBM/fp-go/option"
)
type (
Street struct {
Name string
Number int
}
Address struct {
Street Street
Postcode string
}
AddressBook struct {
Addresses []Address
}
)
func getAddresses(ab AddressBook) []Address {
return ab.Addresses
}
func getStreet(s Address) Street {
return s.Street
}
var FirstAddressStreet = F.Flow3(
getAddresses,
A.Head[Address],
O.Map(getStreet),
)
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})
}

View 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

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-08-11 11:38:30.5947727 +0200 CEST m=+0.033600301
// 2023-08-17 22:59:23.6923568 +0200 CEST m=+0.056359401
package tuple
@@ -205,6 +205,13 @@ func FromArray1[F1 ~func(R) T1, T1, R any](f1 F1) func(r []R) Tuple1[T1] {
}
}
// Push1 creates a [Tuple2] from a [Tuple1] by appending a constant value
func Push1[T1, T2 any](value T2) func(Tuple1[T1]) Tuple2[T1, T2] {
return func(t Tuple1[T1]) Tuple2[T1, T2] {
return MakeTuple2(t.F1, value)
}
}
// MakeTuple2 is a function that converts its 2 parameters into a [Tuple2]
func MakeTuple2[T1, T2 any](t1 T1, t2 T2) Tuple2[T1, T2] {
return Tuple2[T1, T2]{t1, t2}
@@ -315,6 +322,13 @@ func FromArray2[F1 ~func(R) T1, F2 ~func(R) T2, T1, T2, R any](f1 F1, f2 F2) fun
}
}
// Push2 creates a [Tuple3] from a [Tuple2] by appending a constant value
func Push2[T1, T2, T3 any](value T3) func(Tuple2[T1, T2]) Tuple3[T1, T2, T3] {
return func(t Tuple2[T1, T2]) Tuple3[T1, T2, T3] {
return MakeTuple3(t.F1, t.F2, value)
}
}
// MakeTuple3 is a function that converts its 3 parameters into a [Tuple3]
func MakeTuple3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3) Tuple3[T1, T2, T3] {
return Tuple3[T1, T2, T3]{t1, t2, t3}
@@ -436,6 +450,13 @@ func FromArray3[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, T1, T2, T3, R an
}
}
// Push3 creates a [Tuple4] from a [Tuple3] by appending a constant value
func Push3[T1, T2, T3, T4 any](value T4) func(Tuple3[T1, T2, T3]) Tuple4[T1, T2, T3, T4] {
return func(t Tuple3[T1, T2, T3]) Tuple4[T1, T2, T3, T4] {
return MakeTuple4(t.F1, t.F2, t.F3, value)
}
}
// MakeTuple4 is a function that converts its 4 parameters into a [Tuple4]
func MakeTuple4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4) Tuple4[T1, T2, T3, T4] {
return Tuple4[T1, T2, T3, T4]{t1, t2, t3, t4}
@@ -568,6 +589,13 @@ func FromArray4[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
}
}
// Push4 creates a [Tuple5] from a [Tuple4] by appending a constant value
func Push4[T1, T2, T3, T4, T5 any](value T5) func(Tuple4[T1, T2, T3, T4]) Tuple5[T1, T2, T3, T4, T5] {
return func(t Tuple4[T1, T2, T3, T4]) Tuple5[T1, T2, T3, T4, T5] {
return MakeTuple5(t.F1, t.F2, t.F3, t.F4, value)
}
}
// MakeTuple5 is a function that converts its 5 parameters into a [Tuple5]
func MakeTuple5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Tuple5[T1, T2, T3, T4, T5] {
return Tuple5[T1, T2, T3, T4, T5]{t1, t2, t3, t4, t5}
@@ -711,6 +739,13 @@ func FromArray5[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
}
}
// Push5 creates a [Tuple6] from a [Tuple5] by appending a constant value
func Push5[T1, T2, T3, T4, T5, T6 any](value T6) func(Tuple5[T1, T2, T3, T4, T5]) Tuple6[T1, T2, T3, T4, T5, T6] {
return func(t Tuple5[T1, T2, T3, T4, T5]) Tuple6[T1, T2, T3, T4, T5, T6] {
return MakeTuple6(t.F1, t.F2, t.F3, t.F4, t.F5, value)
}
}
// MakeTuple6 is a function that converts its 6 parameters into a [Tuple6]
func MakeTuple6[T1, T2, T3, T4, T5, T6 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Tuple6[T1, T2, T3, T4, T5, T6] {
return Tuple6[T1, T2, T3, T4, T5, T6]{t1, t2, t3, t4, t5, t6}
@@ -865,6 +900,13 @@ func FromArray6[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
}
}
// Push6 creates a [Tuple7] from a [Tuple6] by appending a constant value
func Push6[T1, T2, T3, T4, T5, T6, T7 any](value T7) func(Tuple6[T1, T2, T3, T4, T5, T6]) Tuple7[T1, T2, T3, T4, T5, T6, T7] {
return func(t Tuple6[T1, T2, T3, T4, T5, T6]) Tuple7[T1, T2, T3, T4, T5, T6, T7] {
return MakeTuple7(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, value)
}
}
// MakeTuple7 is a function that converts its 7 parameters into a [Tuple7]
func MakeTuple7[T1, T2, T3, T4, T5, T6, T7 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Tuple7[T1, T2, T3, T4, T5, T6, T7] {
return Tuple7[T1, T2, T3, T4, T5, T6, T7]{t1, t2, t3, t4, t5, t6, t7}
@@ -1030,6 +1072,13 @@ func FromArray7[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
}
}
// Push7 creates a [Tuple8] from a [Tuple7] by appending a constant value
func Push7[T1, T2, T3, T4, T5, T6, T7, T8 any](value T8) func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] {
return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] {
return MakeTuple8(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, value)
}
}
// MakeTuple8 is a function that converts its 8 parameters into a [Tuple8]
func MakeTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] {
return Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]{t1, t2, t3, t4, t5, t6, t7, t8}
@@ -1206,6 +1255,13 @@ func FromArray8[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
}
}
// Push8 creates a [Tuple9] from a [Tuple8] by appending a constant value
func Push8[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](value T9) func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
return MakeTuple9(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, value)
}
}
// MakeTuple9 is a function that converts its 9 parameters into a [Tuple9]
func MakeTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
return Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]{t1, t2, t3, t4, t5, t6, t7, t8, t9}
@@ -1393,6 +1449,13 @@ func FromArray9[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4,
}
}
// Push9 creates a [Tuple10] from a [Tuple9] by appending a constant value
func Push9[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](value T10) func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] {
return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] {
return MakeTuple10(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, value)
}
}
// MakeTuple10 is a function that converts its 10 parameters into a [Tuple10]
func MakeTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] {
return Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10}

View File

@@ -17,6 +17,10 @@
// consider to use arrays for simplicity
package tuple
func Of[T1 any](t T1) Tuple1[T1] {
return MakeTuple1(t)
}
func First[T1, T2 any](t Tuple2[T1, T2]) T1 {
return t.F1
}
@@ -29,7 +33,7 @@ func Swap[T1, T2 any](t Tuple2[T1, T2]) Tuple2[T2, T1] {
return MakeTuple2(t.F2, t.F1)
}
func Of[T1, T2 any](e T2) func(T1) Tuple2[T1, T2] {
func Of2[T1, T2 any](e T2) func(T1) Tuple2[T1, T2] {
return func(t T1) Tuple2[T1, T2] {
return MakeTuple2(t, e)
}