1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-09-01 19:56:12 +02:00

Compare commits

..

8 Commits

Author SHA1 Message Date
Dr. Carsten Leue
7c12b72db1 fix: add missing IOO.FromIOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-19 12:24:05 +02:00
Carsten Leue
dc894ad643 Merge pull request #48 from IBM/cleue-some-benchmarking
fix: add some benchmarks
2023-09-19 10:22:04 +02:00
Dr. Carsten Leue
c902058320 fix: add some benchmarks
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-19 10:21:16 +02:00
Carsten Leue
bf33f4fb66 Merge pull request #46 from a-lipson/main
Modified doc for generateTupled functions (& changed occurences)
2023-09-19 10:12:57 +02:00
Dre
b4d2a5c6be fixed typo in ioeither ChainFirstIOK doc line 2023-09-18 15:48:00 -07:00
a-lipson
705b71d95c Modified doc for generateTupled functions (& changed occurences) 2023-09-15 17:01:11 -07:00
Carsten Leue
34844bcfc2 Merge pull request #45 from IBM/cleue-prefer-second-over-SK
doc: ad doc to SK function
2023-09-13 15:04:50 +02:00
Dr. Carsten Leue
9a9d13b066 doc: ad doc to SK function
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2023-09-13 15:04:19 +02:00
14 changed files with 331 additions and 21 deletions

View File

@@ -338,7 +338,7 @@ func generateUntupled(f *os.File, i int) {
func generateTupled(f *os.File, i int) {
// Create the optionize version
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters returning into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i)
fmt.Fprintf(f, "func Tupled%d[F ~func(", i)
for j := 0; j < i; j++ {
if j > 0 {

View File

@@ -26,6 +26,7 @@ func Bind2nd[T1, T2, R any](f func(T1, T2) R, t2 T2) func(T1) R {
}
}
// SK function (SKI combinator calculus).
func SK[T1, T2 any](_ T1, t2 T2) T2 {
return t2
}

View File

@@ -62,6 +62,7 @@ func First[T1, T2 any](t1 T1, _ T2) T1 {
}
// Second returns the second out of two input values
// Identical to [SK]
func Second[T1, T2 any](_ T1, t2 T2) T2 {
return t2
}

View File

@@ -75,10 +75,20 @@ func ChainIOK[E, A, B any](f func(A) I.IO[B]) func(IOEither[E, A]) IOEither[E, B
return G.ChainIOK[IOEither[E, A], IOEither[E, B]](f)
}
func ChainLazyK[E, A, B any](f func(A) L.Lazy[B]) func(IOEither[E, A]) IOEither[E, B] {
return G.ChainIOK[IOEither[E, A], IOEither[E, B]](f)
}
// FromIO creates an [IOEither] from an [IO] instance, invoking [IO] for each invocation of [IOEither]
func FromIO[E, A any](mr I.IO[A]) IOEither[E, A] {
return G.FromIO[IOEither[E, A]](mr)
}
// FromLazy creates an [IOEither] from a [Lazy] instance, invoking [Lazy] for each invocation of [IOEither]
func FromLazy[E, A any](mr L.Lazy[A]) IOEither[E, A] {
return G.FromIO[IOEither[E, A]](mr)
}
func MonadMap[E, A, B any](fa IOEither[E, A], f func(A) B) IOEither[E, B] {
return G.MonadMap[IOEither[E, A], IOEither[E, B]](fa, f)
}
@@ -167,27 +177,27 @@ func MonadChainTo[E, A, B any](fa IOEither[E, A], fb IOEither[E, B]) IOEither[E,
return G.MonadChainTo(fa, fb)
}
// ChainTo composes to the second monad ignoring the return value of the first
// ChainTo composes to the second [IOEither] monad ignoring the return value of the first
func ChainTo[E, A, B any](fb IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] {
return G.ChainTo[IOEither[E, A]](fb)
}
// MonadChainFirst runs the monad returned by the function but returns the result of the original monad
// MonadChainFirst runs the [IOEither] monad returned by the function but returns the result of the original monad
func MonadChainFirst[E, A, B any](ma IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, A] {
return G.MonadChainFirst(ma, f)
}
// ChainFirst runs the monad returned by the function but returns the result of the original monad
// ChainFirst runs the [IOEither] monad returned by the function but returns the result of the original monad
func ChainFirst[E, A, B any](f func(A) IOEither[E, B]) func(IOEither[E, A]) IOEither[E, A] {
return G.ChainFirst[IOEither[E, A]](f)
}
// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad
// MonadChainFirstIOK runs [IO] the monad returned by the function but returns the result of the original monad
func MonadChainFirstIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, A] {
return G.MonadChainFirstIOK(ma, f)
}
// ChainFirsIOKt runs the monad returned by the function but returns the result of the original monad
// ChainFirstIOK runs the [IO] monad returned by the function but returns the result of the original monad
func ChainFirstIOK[E, A, B any](f func(A) I.IO[B]) func(IOEither[E, A]) IOEither[E, A] {
return G.ChainFirstIOK[IOEither[E, A]](f)
}

View File

@@ -18,6 +18,7 @@ package generic
import (
"time"
ET "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
FI "github.com/IBM/fp-go/internal/fromio"
"github.com/IBM/fp-go/internal/optiont"
@@ -55,6 +56,21 @@ func FromOption[GA ~func() O.Option[A], A any](o O.Option[A]) GA {
return IO.Of[GA](o)
}
func FromEither[GA ~func() O.Option[A], E, A any](e ET.Either[E, A]) GA {
return F.Pipe2(
e,
ET.ToOption[E, A],
FromOption[GA],
)
}
func FromIOEither[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA {
return F.Pipe1(
ioe,
IO.Map[GEA, GA](ET.ToOption[E, A]),
)
}
func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) B) GB {
return optiont.MonadMap(IO.MonadMap[GA, GB, O.Option[A], O.Option[B]], fa, f)
}

View File

@@ -16,7 +16,9 @@
package iooption
import (
ET "github.com/IBM/fp-go/either"
I "github.com/IBM/fp-go/io"
IOE "github.com/IBM/fp-go/ioeither"
G "github.com/IBM/fp-go/iooption/generic"
O "github.com/IBM/fp-go/option"
)
@@ -117,7 +119,7 @@ func Memoize[A any](ma IOOption[A]) IOOption[A] {
return G.Memoize(ma)
}
// Fold convers an IOOption into an IO
// Fold convers an [IOOption] into an [IO]
func Fold[A, B any](onNone func() I.IO[B], onSome func(A) I.IO[B]) func(IOOption[A]) I.IO[B] {
return G.Fold[IOOption[A]](onNone, onSome)
}
@@ -126,3 +128,13 @@ func Fold[A, B any](onNone func() I.IO[B], onSome func(A) I.IO[B]) func(IOOption
func Defer[A any](gen func() IOOption[A]) IOOption[A] {
return G.Defer[IOOption[A]](gen)
}
// FromIOEither converts an [IOEither] into an [IOOption]
func FromIOEither[E, A any](ioe IOE.IOEither[E, A]) IOOption[A] {
return G.FromIOEither[IOOption[A]](ioe)
}
// FromEither converts an [Either] into an [IOOption]
func FromEither[E, A any](e ET.Either[E, A]) IOOption[A] {
return G.FromEither[IOOption[A]](e)
}

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 stateless
import (
"testing"
F "github.com/IBM/fp-go/function"
)
func BenchmarkMulti(b *testing.B) {
// run the Fib function b.N times
for n := 0; n < b.N; n++ {
single()
}
}
func single() int64 {
length := 10000
nums := make([]int, 0, length)
for i := 0; i < length; i++ {
nums = append(nums, i+1)
}
return F.Pipe6(
nums,
FromArray[int],
Filter(func(n int) bool {
return n%2 == 0
}),
Map(func(t int) int64 {
return int64(t)
}),
Filter(func(t int64) bool {
n := t
for n/10 != 0 {
if n%10 == 4 {
return false
}
n = n / 10
}
return true
}),
Map(func(t int64) int {
return int(t)
}),
Reduce(func(n int64, r int) int64 {
return n + int64(r)
}, int64(0)),
)
}

View File

@@ -82,6 +82,7 @@ func Map[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU,
m,
)
}
m = O.Map(T.Map2(recurse, f))
return recurse

View File

@@ -74,9 +74,7 @@ func TestMultipleHttpRequests(t *testing.T) {
assert.Equal(t, E.Of[error](count), result())
}
// TestHeterogeneousHttpRequests shows how to execute multiple HTTP requests in parallel when
// the response structure of these requests is different. We use [R.TraverseTuple2] to account for the different types
func TestHeterogeneousHttpRequests(t *testing.T) {
func heterogeneousHttpRequests() R.ReaderIOEither[T.Tuple2[PostItem, CatFact]] {
// prepare the http client
client := H.MakeClient(HTTP.DefaultClient)
// readSinglePost sends a GET request and parses the response as [PostItem]
@@ -84,7 +82,7 @@ func TestHeterogeneousHttpRequests(t *testing.T) {
// readSingleCatFact sends a GET request and parses the response as [CatFact]
readSingleCatFact := H.ReadJson[CatFact](client)
data := F.Pipe3(
return F.Pipe3(
T.MakeTuple2("https://jsonplaceholder.typicode.com/posts/1", "https://catfact.ninja/fact"),
T.Map2(H.MakeGetRequest, H.MakeGetRequest),
R.TraverseTuple2(
@@ -94,7 +92,21 @@ func TestHeterogeneousHttpRequests(t *testing.T) {
R.ChainFirstIOK(IO.Logf[T.Tuple2[PostItem, CatFact]]("Log Result: %v")),
)
}
// TestHeterogeneousHttpRequests shows how to execute multiple HTTP requests in parallel when
// the response structure of these requests is different. We use [R.TraverseTuple2] to account for the different types
func TestHeterogeneousHttpRequests(t *testing.T) {
data := heterogeneousHttpRequests()
result := data(context.Background())
fmt.Println(result())
}
// BenchmarkHeterogeneousHttpRequests shows how to execute multiple HTTP requests in parallel when
// the response structure of these requests is different. We use [R.TraverseTuple2] to account for the different types
func BenchmarkHeterogeneousHttpRequests(b *testing.B) {
heterogeneousHttpRequests()(context.Background())
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tuples
import (
"fmt"
"os"
"path/filepath"
"testing"
F "github.com/IBM/fp-go/function"
IOE "github.com/IBM/fp-go/ioeither"
IOEF "github.com/IBM/fp-go/ioeither/file"
IOEG "github.com/IBM/fp-go/ioeither/generic"
IOO "github.com/IBM/fp-go/iooption"
)
func TestIOEitherToOption1(t *testing.T) {
tmpDir := t.TempDir()
content := []byte("abc")
resIOO := F.Pipe2(
content,
IOEF.WriteFile(filepath.Join(tmpDir, "test.txt"), os.ModePerm),
IOEG.Fold[IOE.IOEither[error, []byte]](
IOO.Of[error],
F.Ignore1of1[[]byte](IOO.None[error]),
),
)
fmt.Println(resIOO())
}
func TestIOEitherToOption2(t *testing.T) {
tmpDir := t.TempDir()
content := []byte("abc")
resIOO := F.Pipe3(
content,
IOEF.WriteFile(filepath.Join(tmpDir, "test.txt"), os.ModePerm),
IOE.Swap[error, []byte],
IOO.FromIOEither[[]byte, error],
)
fmt.Println(resIOO())
}

View File

@@ -0,0 +1 @@
abc

View File

@@ -0,0 +1,130 @@
// 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 tuples
import (
"bytes"
"io"
"strings"
"testing"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
IOE "github.com/IBM/fp-go/ioeither"
IOEF "github.com/IBM/fp-go/ioeither/file"
T "github.com/IBM/fp-go/tuple"
"github.com/stretchr/testify/assert"
)
func sampleConvertDocx(r io.Reader) (string, map[string]string, error) {
content, err := io.ReadAll(r)
return string(content), map[string]string{}, err
}
func TestSampleConvertDocx1(t *testing.T) {
// this conversion approach has the disadvantage that it exhausts the reader
// so we cannot invoke the resulting IOEither multiple times
convertDocx := func(r io.Reader) IOE.IOEither[error, T.Tuple2[string, map[string]string]] {
return IOE.TryCatchError(func() (T.Tuple2[string, map[string]string], error) {
text, meta, err := sampleConvertDocx(r)
return T.MakeTuple2(text, meta), err
})
}
rdr := strings.NewReader("abc")
resIOE := convertDocx(rdr)
resE := resIOE()
assert.True(t, E.IsRight(resE))
}
func TestSampleConvertDocx2(t *testing.T) {
// this approach assumes that `sampleConvertDocx` does not have any side effects
// other than reading from a `Reader`. As a consequence it can be a pure function itself.
// The disadvantage is that its input has to exist in memory which is probably not a good
// idea for large inputs
convertDocx := func(data []byte) E.Either[error, T.Tuple2[string, map[string]string]] {
return E.TryCatchError(func() (T.Tuple2[string, map[string]string], error) {
text, meta, err := sampleConvertDocx(bytes.NewReader(data))
return T.MakeTuple2(text, meta), err
})
}
resE := convertDocx([]byte("abc"))
assert.True(t, E.IsRight(resE))
}
// onClose closes a closeable resource
func onClose[R io.Closer](r R) IOE.IOEither[error, R] {
return IOE.TryCatchError(func() (R, error) {
return r, r.Close()
})
}
// convertDocx3 takes an `acquire` function that creates an instance or a [ReaderCloser] whenever the resulting [IOEither] is invoked. Since
// we return a [Closer] the instance will be closed after use, automatically. This design makes sure that the resulting [IOEither] can be invoked
// as many times as necessary
func convertDocx3[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, T.Tuple2[string, map[string]string]] {
return IOE.WithResource[T.Tuple2[string, map[string]string]](
acquire,
onClose[R])(
func(r R) IOE.IOEither[error, T.Tuple2[string, map[string]string]] {
return IOE.TryCatchError(func() (T.Tuple2[string, map[string]string], error) {
text, meta, err := sampleConvertDocx(r)
return T.MakeTuple2(text, meta), err
})
},
)
}
// convertDocx4 takes an `acquire` function that creates an instance or a [Reader] whenever the resulting [IOEither] is invoked.
// This design makes sure that the resulting [IOEither] can be invoked
// as many times as necessary
func convertDocx4[R io.Reader](acquire IOE.IOEither[error, R]) IOE.IOEither[error, T.Tuple2[string, map[string]string]] {
return F.Pipe1(
acquire,
IOE.Chain(func(r R) IOE.IOEither[error, T.Tuple2[string, map[string]string]] {
return IOE.TryCatchError(func() (T.Tuple2[string, map[string]string], error) {
text, meta, err := sampleConvertDocx(r)
return T.MakeTuple2(text, meta), err
})
}),
)
}
func TestSampleConvertDocx3(t *testing.T) {
// IOEither that creates the reader
acquire := IOEF.Open("./samples/data.txt")
resIOE := convertDocx3(acquire)
resE := resIOE()
assert.True(t, E.IsRight(resE))
}
func TestSampleConvertDocx4(t *testing.T) {
// IOEither that creates the reader
acquire := IOE.FromIO[error](func() *strings.Reader {
return strings.NewReader("abc")
})
resIOE := convertDocx4(acquire)
resE := resIOE()
assert.True(t, E.IsRight(resE))
}

View File

@@ -111,7 +111,7 @@ func MakeTuple1[T1 any](t1 T1) Tuple1[T1] {
return Tuple1[T1]{t1}
}
// Tupled1 converts a function with 1 parameters returning into a function taking a Tuple1
// Tupled1 converts a function with 1 parameters into a function taking a Tuple1
// The inverse function is [Untupled1]
func Tupled1[F ~func(T1) R, T1, R any](f F) func(Tuple1[T1]) R {
return func(t Tuple1[T1]) R {
@@ -217,7 +217,7 @@ func MakeTuple2[T1, T2 any](t1 T1, t2 T2) Tuple2[T1, T2] {
return Tuple2[T1, T2]{t1, t2}
}
// Tupled2 converts a function with 2 parameters returning into a function taking a Tuple2
// Tupled2 converts a function with 2 parameters into a function taking a Tuple2
// The inverse function is [Untupled2]
func Tupled2[F ~func(T1, T2) R, T1, T2, R any](f F) func(Tuple2[T1, T2]) R {
return func(t Tuple2[T1, T2]) R {
@@ -334,7 +334,7 @@ func MakeTuple3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3) Tuple3[T1, T2, T3] {
return Tuple3[T1, T2, T3]{t1, t2, t3}
}
// Tupled3 converts a function with 3 parameters returning into a function taking a Tuple3
// Tupled3 converts a function with 3 parameters into a function taking a Tuple3
// The inverse function is [Untupled3]
func Tupled3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(Tuple3[T1, T2, T3]) R {
return func(t Tuple3[T1, T2, T3]) R {
@@ -462,7 +462,7 @@ func MakeTuple4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4) Tuple4[T1, T2, T
return Tuple4[T1, T2, T3, T4]{t1, t2, t3, t4}
}
// Tupled4 converts a function with 4 parameters returning into a function taking a Tuple4
// Tupled4 converts a function with 4 parameters into a function taking a Tuple4
// The inverse function is [Untupled4]
func Tupled4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(Tuple4[T1, T2, T3, T4]) R {
return func(t Tuple4[T1, T2, T3, T4]) R {
@@ -601,7 +601,7 @@ func MakeTuple5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Tuple
return Tuple5[T1, T2, T3, T4, T5]{t1, t2, t3, t4, t5}
}
// Tupled5 converts a function with 5 parameters returning into a function taking a Tuple5
// Tupled5 converts a function with 5 parameters into a function taking a Tuple5
// The inverse function is [Untupled5]
func Tupled5[F ~func(T1, T2, T3, T4, T5) R, T1, T2, T3, T4, T5, R any](f F) func(Tuple5[T1, T2, T3, T4, T5]) R {
return func(t Tuple5[T1, T2, T3, T4, T5]) R {
@@ -751,7 +751,7 @@ func MakeTuple6[T1, T2, T3, T4, T5, T6 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t
return Tuple6[T1, T2, T3, T4, T5, T6]{t1, t2, t3, t4, t5, t6}
}
// Tupled6 converts a function with 6 parameters returning into a function taking a Tuple6
// Tupled6 converts a function with 6 parameters into a function taking a Tuple6
// The inverse function is [Untupled6]
func Tupled6[F ~func(T1, T2, T3, T4, T5, T6) R, T1, T2, T3, T4, T5, T6, R any](f F) func(Tuple6[T1, T2, T3, T4, T5, T6]) R {
return func(t Tuple6[T1, T2, T3, T4, T5, T6]) R {
@@ -912,7 +912,7 @@ func MakeTuple7[T1, T2, T3, T4, T5, T6, T7 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T
return Tuple7[T1, T2, T3, T4, T5, T6, T7]{t1, t2, t3, t4, t5, t6, t7}
}
// Tupled7 converts a function with 7 parameters returning into a function taking a Tuple7
// Tupled7 converts a function with 7 parameters into a function taking a Tuple7
// The inverse function is [Untupled7]
func Tupled7[F ~func(T1, T2, T3, T4, T5, T6, T7) R, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) R {
return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) R {
@@ -1084,7 +1084,7 @@ func MakeTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 T1, t2 T2, t3 T3, t4 T4,
return Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]{t1, t2, t3, t4, t5, t6, t7, t8}
}
// Tupled8 converts a function with 8 parameters returning into a function taking a Tuple8
// Tupled8 converts a function with 8 parameters into a function taking a Tuple8
// The inverse function is [Untupled8]
func Tupled8[F ~func(T1, T2, T3, T4, T5, T6, T7, T8) R, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) R {
return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) R {
@@ -1267,7 +1267,7 @@ func MakeTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 T1, t2 T2, t3 T3, t4
return Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]{t1, t2, t3, t4, t5, t6, t7, t8, t9}
}
// Tupled9 converts a function with 9 parameters returning into a function taking a Tuple9
// Tupled9 converts a function with 9 parameters into a function taking a Tuple9
// The inverse function is [Untupled9]
func Tupled9[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) R {
return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) R {
@@ -1461,7 +1461,7 @@ func MakeTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 T1, t2 T2, t3 T
return Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10}
}
// Tupled10 converts a function with 10 parameters returning into a function taking a Tuple10
// Tupled10 converts a function with 10 parameters into a function taking a Tuple10
// The inverse function is [Untupled10]
func Tupled10[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) R {
return func(t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) R {

View File

@@ -21,10 +21,12 @@ func Of[T1 any](t T1) Tuple1[T1] {
return MakeTuple1(t)
}
// First returns the first element of a [Tuple2]
func First[T1, T2 any](t Tuple2[T1, T2]) T1 {
return t.F1
}
// Second returns the second element of a [Tuple2]
func Second[T1, T2 any](t Tuple2[T1, T2]) T2 {
return t.F2
}