From 52b71ef4f3a5a7064aaeecc66a8fe5387e67350d Mon Sep 17 00:00:00 2001 From: "Dr. Carsten Leue" Date: Tue, 5 Sep 2023 22:51:46 +0200 Subject: [PATCH] fix: add more examples Signed-off-by: Dr. Carsten Leue --- ord/ord.go | 10 +- .../chapter08_tupperware_test.go | 143 ++++++++++++++++++ .../chapter09_monadiconions_test.go | 97 +++++++++++- string/string.go | 8 +- 4 files changed, 245 insertions(+), 13 deletions(-) diff --git a/ord/ord.go b/ord/ord.go index dce7c0f..5ad1441 100644 --- a/ord/ord.go +++ b/ord/ord.go @@ -131,7 +131,7 @@ func FromStrictCompare[A C.Ordered]() Ord[A] { return MakeOrd(strictCompare[A], strictEq[A]) } -// Lt tests 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 { @@ -140,7 +140,7 @@ func Lt[A any](O Ord[A]) func(A) func(A) bool { } } -// Leq Tests 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 { @@ -150,9 +150,9 @@ func Leq[A any](O Ord[A]) func(A) func(A) bool { } /** - * Test whether one value is _strictly greater than_ another + * Test whether one value is strictly greater than another */ -func cc[A any](O Ord[A]) func(A) func(A) bool { +func Gt[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 @@ -160,7 +160,7 @@ func cc[A any](O Ord[A]) func(A) func(A) bool { } } -// Geq tests 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 { diff --git a/samples/mostly-adequate/chapter08_tupperware_test.go b/samples/mostly-adequate/chapter08_tupperware_test.go index bd41f40..567243b 100644 --- a/samples/mostly-adequate/chapter08_tupperware_test.go +++ b/samples/mostly-adequate/chapter08_tupperware_test.go @@ -19,9 +19,12 @@ import ( "fmt" "time" + A "github.com/IBM/fp-go/array" E "github.com/IBM/fp-go/either" "github.com/IBM/fp-go/errors" F "github.com/IBM/fp-go/function" + I "github.com/IBM/fp-go/identity" + IOE "github.com/IBM/fp-go/ioeither" N "github.com/IBM/fp-go/number" O "github.com/IBM/fp-go/option" "github.com/IBM/fp-go/ord" @@ -40,6 +43,45 @@ func getBalance(a Account) float32 { return a.Balance } +type ( + Chapter08User struct { + Id int + Name string + Active bool + Saved bool + } +) + +var ( + albert08 = Chapter08User{ + Id: 1, + Active: true, + Name: "Albert", + } + + gary08 = Chapter08User{ + Id: 2, + Active: false, + Name: "Gary", + } + + theresa08 = Chapter08User{ + Id: 3, + Active: true, + Name: "Theresa", + } + + yi08 = Chapter08User{Id: 4, Name: "Yi", Active: true} +) + +func (u Chapter08User) getName() string { + return u.Name +} + +func (u Chapter08User) isActive() bool { + return u.Active +} + var ( ordFloat32 = ord.FromStrictCompare[float32]() UpdateLedger = F.Identity[Account] @@ -55,6 +97,33 @@ var ( Withdraw(20), O.Fold(F.Constant("You're broke!"), FinishTransaction), ) + + // showWelcome :: User -> String + showWelcome = F.Flow2( + Chapter08User.getName, + S.Format[string]("Welcome %s"), + ) + + // checkActive :: User -> Either error User + checkActive = E.FromPredicate(Chapter08User.isActive, F.Constant1[Chapter08User](fmt.Errorf("Your account is not active"))) + + // validateUser :: (User -> Either String ()) -> User -> Either String User + validateUser = F.Curry2(func(validate func(Chapter08User) E.Either[error, any], user Chapter08User) E.Either[error, Chapter08User] { + return F.Pipe2( + user, + validate, + E.MapTo[error, any](user), + ) + }) + + // save :: User -> IOEither error User + save = func(user Chapter08User) IOE.IOEither[error, Chapter08User] { + return IOE.FromIO[error](func() Chapter08User { + var u = user + u.Saved = true + return u + }) + } ) func Withdraw(amount float32) func(account Account) O.Option[Account] { @@ -131,3 +200,77 @@ func Example_getAge() { // Left[*time.ParseError, float64](parsing time "July 4, 2001" as "2006-01-02": cannot parse "July 4, 2001" as "2006") // If you survive, you will be 6837 } + +func Example_solution08A() { + incrF := I.Map(N.Add(1)) + + fmt.Println(incrF(I.Of(2))) + + // Output: 3 +} + +func Example_solution08B() { + // initial :: User -> Option rune + initial := F.Flow3( + Chapter08User.getName, + S.ToRunes, + A.Head[rune], + ) + + fmt.Println(initial(albert08)) + + // Output: + // Some[int32](65) +} + +func Example_solution08C() { + + // eitherWelcome :: User -> Either String String + eitherWelcome := F.Flow2( + checkActive, + E.Map[error](showWelcome), + ) + + fmt.Println(eitherWelcome(gary08)) + fmt.Println(eitherWelcome(theresa08)) + + // Output: + // Left[*errors.errorString, string](Your account is not active) + // Right[, string](Welcome Theresa) +} + +func Example_solution08D() { + + // // validateName :: User -> Either String () + validateName := F.Flow3( + Chapter08User.getName, + E.FromPredicate(F.Flow2( + S.Size, + ord.Gt(ord.FromStrictCompare[int]())(3), + ), errors.OnSome[string]("Your name %s is larger than 3 characters")), + E.Map[error](F.ToAny[string]), + ) + + saveAndWelcome := F.Flow2( + save, + IOE.Map[error](showWelcome), + ) + + register := F.Flow3( + validateUser(validateName), + IOE.FromEither[error, Chapter08User], + IOE.Chain(saveAndWelcome), + ) + + fmt.Println(validateName(gary08)) + fmt.Println(validateName(yi08)) + + fmt.Println(register(albert08)()) + fmt.Println(register(yi08)()) + + // Output: + // Right[, string](Gary) + // Left[*errors.errorString, ](Your name Yi is larger than 3 characters) + // Right[, string](Welcome Albert) + // Left[*errors.errorString, string](Your name Yi is larger than 3 characters) +} diff --git a/samples/mostly-adequate/chapter09_monadiconions_test.go b/samples/mostly-adequate/chapter09_monadiconions_test.go index e6a435a..0e54b5b 100644 --- a/samples/mostly-adequate/chapter09_monadiconions_test.go +++ b/samples/mostly-adequate/chapter09_monadiconions_test.go @@ -17,10 +17,13 @@ package mostlyadequate import ( "fmt" + "path" A "github.com/IBM/fp-go/array" F "github.com/IBM/fp-go/function" + "github.com/IBM/fp-go/io" O "github.com/IBM/fp-go/option" + S "github.com/IBM/fp-go/string" ) type ( @@ -37,20 +40,70 @@ type ( AddressBook struct { Addresses []Address } + + Chapter09User struct { + Id int + Name string + Address Address + } ) -func getAddresses(ab AddressBook) []Address { +var ( + albert09 = Chapter09User{ + Id: 1, + Name: "Albert", + Address: Address{ + Street: Street{ + Number: 22, + Name: "Walnut St", + }, + }, + } + + gary09 = Chapter09User{ + Id: 2, + Name: "Gary", + Address: Address{ + Street: Street{ + Number: 14, + }, + }, + } + + theresa09 = Chapter09User{ + Id: 3, + Name: "Theresa", + } +) + +func (ab AddressBook) getAddresses() []Address { return ab.Addresses } -func getStreet(s Address) Street { +func (s Address) getStreet() Street { return s.Street } -var FirstAddressStreet = F.Flow3( - getAddresses, - A.Head[Address], - O.Map(getStreet), +func (s Street) getName() string { + return s.Name +} + +func (u Chapter09User) getAddress() Address { + return u.Address +} + +var ( + FirstAddressStreet = F.Flow3( + AddressBook.getAddresses, + A.Head[Address], + O.Map(Address.getStreet), + ) + + // getFile :: IO String + getFile = io.Of("/home/mostly-adequate/ch09.md") + + // pureLog :: String -> IO () + pureLog = io.Logf[string]("%s") ) func Example_street() { @@ -62,3 +115,35 @@ func Example_street() { // Output: // Some[mostlyadequate.Street]({Mulburry 8402}) } + +func Example_solution09A() { + // // getStreetName :: User -> Maybe String + getStreetName := F.Flow4( + Chapter09User.getAddress, + Address.getStreet, + Street.getName, + O.FromPredicate(S.IsNonEmpty), + ) + + fmt.Println(getStreetName(albert09)) + fmt.Println(getStreetName(gary09)) + fmt.Println(getStreetName(theresa09)) + + // Output: + // Some[string](Walnut St) + // None[string] + // None[string] + +} + +func Example_solution09B() { + logFilename := F.Flow2( + io.Map(path.Base), + io.ChainFirst(pureLog), + ) + + fmt.Println(logFilename(getFile)()) + + // Output: + // ch09.md +} diff --git a/string/string.go b/string/string.go index 7f4cc95..5c336af 100644 --- a/string/string.go +++ b/string/string.go @@ -20,7 +20,7 @@ import ( "strings" F "github.com/IBM/fp-go/function" - O "github.com/IBM/fp-go/ord" + "github.com/IBM/fp-go/ord" ) var ( @@ -31,7 +31,7 @@ var ( ToLowerCase = strings.ToLower // Ord implements the default ordering for strings - Ord = O.FromStrictCompare[string]() + Ord = ord.FromStrictCompare[string]() ) func Eq(left string, right string) bool { @@ -42,6 +42,10 @@ func ToBytes(s string) []byte { return []byte(s) } +func ToRunes(s string) []rune { + return []rune(s) +} + func IsEmpty(s string) bool { return len(s) == 0 }