// 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" "path" "regexp" 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" "github.com/IBM/fp-go/io" IOE "github.com/IBM/fp-go/ioeither" O "github.com/IBM/fp-go/option" S "github.com/IBM/fp-go/string" ) type ( Street struct { Name string Number int } Address struct { Street Street Postcode string } AddressBook struct { Addresses []Address } Chapter09User struct { Id int Name string Address 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 (s Address) getStreet() Street { return s.Street } 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") // 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() { s := FirstAddressStreet(AddressBook{ Addresses: A.From(Address{Street: Street{Name: "Mulburry", Number: 8402}, Postcode: "WC2N"}), }) fmt.Println(s) // 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 } 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[, string](sleepy@grandpa.net) // Left[*errors.errorString, string](email notanemail is invalid) }