diff --git a/internal/bindt/bind.go b/internal/bindt/bind.go index 71cb731..b679e95 100644 --- a/internal/bindt/bind.go +++ b/internal/bindt/bind.go @@ -38,3 +38,24 @@ func Bind[SET ~func(B) func(S1) S2, FCT ~func(S1) HKTB, S1, S2, B, HKTS1, HKTS2, T.Ap[HKTB, HKTS2], )) } + +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)), + ) +} diff --git a/ioeither/bind.go b/ioeither/bind.go index 8acd930..c6afcaf 100644 --- a/ioeither/bind.go +++ b/ioeither/bind.go @@ -20,6 +20,18 @@ import ( ) // Bind applies a function to an input state and merges the result into that state -func Bind[S1, S2, E, A any](s func(A) func(S1) S2) func(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) +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) } diff --git a/ioeither/examples_do_test.go b/ioeither/examples_do_test.go new file mode 100644 index 0000000..9424432 --- /dev/null +++ b/ioeither/examples_do_test.go @@ -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[, int](8) +} diff --git a/ioeither/generic/bind.go b/ioeither/generic/bind.go index 1434e28..5f4ef2d 100644 --- a/ioeither/generic/bind.go +++ b/ioeither/generic/bind.go @@ -28,13 +28,40 @@ func Bind[ FCT ~func(S1) GA, E any, SET ~func(A) func(S1) S2, - A, S1, S2 any](s SET) func(FCT) func(GS1) GS2 { - return func(f FCT) func(GS1) GS2 { - return G.Bind( - Chain[GS1, GS2, E, S1, S2], - Map[GA, GS2, E, A, S2], - s, - f, - ) - } + 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, + ) } diff --git a/tuple/tuple.go b/tuple/tuple.go index dede5ce..386b1f1 100644 --- a/tuple/tuple.go +++ b/tuple/tuple.go @@ -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) }