mirror of
https://github.com/IBM/fp-go.git
synced 2025-07-17 01:32:23 +02:00
fix: add more mostly-adequate examples and solutions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@ -27,13 +27,13 @@ HKTRB = HKT<Either[B]>
|
|||||||
HKTA = HKT<A>
|
HKTA = HKT<A>
|
||||||
HKTB = HKT<B>
|
HKTB = HKT<B>
|
||||||
*/
|
*/
|
||||||
func traverse[E, A, B, HKTA, HKTB, HKTRB any](
|
func traverse[E, A, B, HKTB, HKTRB any](
|
||||||
_of func(Either[E, B]) HKTRB,
|
mof func(Either[E, B]) HKTRB,
|
||||||
_map func(HKTB, func(B) Either[E, B]) HKTRB,
|
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
||||||
) func(Either[E, A], func(A) HKTB) HKTRB {
|
) func(Either[E, A], func(A) HKTB) HKTRB {
|
||||||
|
|
||||||
left := F.Flow2(Left[B, E], _of)
|
left := F.Flow2(Left[B, E], mof)
|
||||||
right := F.Bind2nd(_map, Right[E, B])
|
right := mmap(Right[E, B])
|
||||||
|
|
||||||
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
|
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
|
||||||
return MonadFold(ta,
|
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](
|
// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
|
||||||
_of func(Either[E, B]) HKTRB,
|
func Traverse[A, E, B, HKTB, HKTRB any](
|
||||||
_map func(HKTB, func(B) Either[E, B]) HKTRB,
|
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 {
|
) 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 func(f func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||||
return F.Bind2nd(delegate, f)
|
return F.Bind2nd(delegate, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Sequence converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
|
||||||
*
|
|
||||||
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>
|
|
||||||
*/
|
|
||||||
func Sequence[E, A, HKTA, HKTRA any](
|
func Sequence[E, A, HKTA, HKTRA any](
|
||||||
_of func(Either[E, A]) HKTRA,
|
mof func(Either[E, A]) HKTRA,
|
||||||
_map func(HKTA, func(A) Either[E, A]) HKTRA,
|
mmap func(func(A) Either[E, A]) func(HKTA) HKTRA,
|
||||||
) func(Either[E, 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]()
|
return O.None[int]()
|
||||||
}
|
}
|
||||||
trav := Traverse[string, int, int, O.Option[Either[string, int]]](
|
trav := Traverse[int](
|
||||||
O.Of[Either[string, int]],
|
O.Of[Either[string, int]],
|
||||||
O.MonadMap[int, Either[string, int]],
|
O.Map[int, Either[string, int]],
|
||||||
)(f)
|
)(f)
|
||||||
|
|
||||||
assert.Equal(t, O.Of(Left[int]("a")), F.Pipe1(Left[int]("a"), trav))
|
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(
|
seq := Sequence(
|
||||||
O.Of[Either[string, int]],
|
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))))
|
assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1))))
|
||||||
|
@ -19,13 +19,22 @@ import (
|
|||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HKTA = HKT<A>
|
// Sequence converts an [Option] of some higher kinded type into the higher kinded type of an [Option]
|
||||||
// HKTOA = HKT<Option<A>>
|
|
||||||
//
|
|
||||||
// Sequence converts an option of some higher kinded type into the higher kinded type of an option
|
|
||||||
func Sequence[A, HKTA, HKTOA any](
|
func Sequence[A, HKTA, HKTOA any](
|
||||||
_of func(Option[A]) HKTOA,
|
mof func(Option[A]) HKTOA,
|
||||||
_map func(HKTA, func(A) Option[A]) HKTOA,
|
mmap func(func(A) Option[A]) func(HKTA) HKTOA,
|
||||||
) func(Option[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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
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] {
|
func Lookup[M ~map[K]V, K comparable, V any](k K) func(M) O.Option[V] {
|
||||||
n := O.None[V]()
|
n := O.None[V]()
|
||||||
return func(m M) O.Option[V] {
|
return func(m M) O.Option[V] {
|
||||||
|
@ -102,6 +102,11 @@ func Lookup[V any, K comparable](k K) func(map[K]V) O.Option[V] {
|
|||||||
return G.Lookup[map[K]V](k)
|
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
|
// Has tests if a key is contained in a map
|
||||||
func Has[K comparable, V any](k K, r map[K]V) bool {
|
func Has[K comparable, V any](k K, r map[K]V) bool {
|
||||||
return G.Has(k, r)
|
return G.Has(k, r)
|
||||||
|
@ -18,10 +18,14 @@ package mostlyadequate
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
A "github.com/IBM/fp-go/array"
|
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"
|
F "github.com/IBM/fp-go/function"
|
||||||
"github.com/IBM/fp-go/io"
|
"github.com/IBM/fp-go/io"
|
||||||
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
S "github.com/IBM/fp-go/string"
|
S "github.com/IBM/fp-go/string"
|
||||||
)
|
)
|
||||||
@ -104,6 +108,21 @@ var (
|
|||||||
|
|
||||||
// pureLog :: String -> IO ()
|
// pureLog :: String -> IO ()
|
||||||
pureLog = io.Logf[string]("%s")
|
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() {
|
func Example_street() {
|
||||||
@ -147,3 +166,21 @@ func Example_solution09B() {
|
|||||||
// Output:
|
// Output:
|
||||||
// ch09.md
|
// 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)
|
||||||
|
}
|
||||||
|
@ -23,16 +23,65 @@ import (
|
|||||||
R "github.com/IBM/fp-go/context/readerioeither"
|
R "github.com/IBM/fp-go/context/readerioeither"
|
||||||
H "github.com/IBM/fp-go/context/readerioeither/http"
|
H "github.com/IBM/fp-go/context/readerioeither/http"
|
||||||
F "github.com/IBM/fp-go/function"
|
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 {
|
type (
|
||||||
|
PostItem struct {
|
||||||
UserId uint `json:"userId"`
|
UserId uint `json:"userId"`
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Body string `json:"body"`
|
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 getTitle(item PostItem) string {
|
func (player Player) getId() int {
|
||||||
|
return player.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (item PostItem) getTitle() string {
|
||||||
return item.Title
|
return item.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +104,7 @@ func Example_renderPage() {
|
|||||||
idxToUrl,
|
idxToUrl,
|
||||||
H.MakeGetRequest,
|
H.MakeGetRequest,
|
||||||
H.ReadJson[PostItem](client),
|
H.ReadJson[PostItem](client),
|
||||||
R.Map(getTitle),
|
R.Map(PostItem.getTitle),
|
||||||
)
|
)
|
||||||
|
|
||||||
res := F.Pipe2(
|
res := F.Pipe2(
|
||||||
@ -71,3 +120,64 @@ func Example_renderPage() {
|
|||||||
// Right[<nil>, string](<div>Destinations: [qui est esse], Events: [ea molestias quasi exercitationem repellat qui ipsa sit aut]</div>)
|
// 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)))
|
||||||
|
}
|
Reference in New Issue
Block a user