mostlyadequate

package
v1.0.119 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2024 License: Apache-2.0 Imports: 3 Imported by: 0

README

Mostly Adequate: fp-go Companion Guide

This resource is meant to serve as a go "companion" resource to Professor Frisby's Mostly Adequate Guide.

It is a port of the mostly-adequate-fp-ts book.

Documentation

Overview

Package mostlyadequate is meant to serve as a go "companion" resource to Professor Frisby's Mostly Adequate Guide.

Example (Application)
package main

import (
	"context"
	"fmt"
	"net/http"
	"regexp"

	A "github.com/IBM/fp-go/array"
	E "github.com/IBM/fp-go/either"
	F "github.com/IBM/fp-go/function"
	J "github.com/IBM/fp-go/json"
	S "github.com/IBM/fp-go/string"

	R "github.com/IBM/fp-go/context/readerioeither"
	H "github.com/IBM/fp-go/context/readerioeither/http"
)

type (
	FlickrMedia struct {
		Link string `json:"m"`
	}

	FlickrItem struct {
		Media FlickrMedia `json:"media"`
	}

	FlickrFeed struct {
		Items []FlickrItem `json:"items"`
	}
)

func (f FlickrMedia) getLink() string {
	return f.Link
}

func (f FlickrItem) getMedia() FlickrMedia {
	return f.Media
}

func (f FlickrFeed) getItems() []FlickrItem {
	return f.Items
}

func main() {
	// pure
	host := "api.flickr.com"
	path := "/services/feeds/photos_public.gne"
	query := S.Format[string]("?tags=%s&format=json&jsoncallback=?")
	url := F.Flow2(
		query,
		S.Format[string](fmt.Sprintf("https://%s%s%%s", host, path)),
	)
	// flick returns jsonP, we extract the JSON body, this is handled by jquery in the original code
	sanitizeJSONP := Replace(regexp.MustCompile(`(?s)^\s*\((.*)\)\s*$`))("$1")
	// parse jsonP
	parseJSONP := F.Flow3(
		sanitizeJSONP,
		S.ToBytes,
		J.Unmarshal[FlickrFeed],
	)
	// markup
	img := S.Format[string]("<img src='%s'/>")
	// lenses
	mediaURL := F.Flow2(
		FlickrItem.getMedia,
		FlickrMedia.getLink,
	)
	mediaURLs := F.Flow2(
		FlickrFeed.getItems,
		A.Map(mediaURL),
	)
	images := F.Flow2(
		mediaURLs,
		A.Map(img),
	)

	client := H.MakeClient(http.DefaultClient)

	// func(string) R.ReaderIOEither[[]string]
	app := F.Flow5(
		url,
		H.MakeGetRequest,
		H.ReadText(client),
		R.ChainEitherK(parseJSONP),
		R.Map(images),
	)

	// R.ReaderIOEither[[]string]
	// this is the managed effect that can be called to download and render the images
	catImageEffect := app("cats")

	// impure, actually executes the effect
	catImages := catImageEffect(context.TODO())()
	fmt.Println(E.IsRight(catImages))

}
Output:

true
Example (Dasherize)
fmt.Println(Dasherize("The world is a vampire"))
Output:

the-world-is-a-vampire
Example (Flock)
package main

import "fmt"

type Flock struct {
	Seagulls int
}

func MakeFlock(n int) Flock {
	return Flock{Seagulls: n}
}

func (f *Flock) Conjoin(other *Flock) *Flock {
	f.Seagulls += other.Seagulls
	return f
}

func (f *Flock) Breed(other *Flock) *Flock {
	f.Seagulls = f.Seagulls * other.Seagulls
	return f
}

func main() {

	flockA := MakeFlock(4)
	flockB := MakeFlock(2)
	flockC := MakeFlock(0)

	fmt.Println(flockA.Conjoin(&flockC).Breed(&flockB).Conjoin(flockA.Breed(&flockB)).Seagulls)

}
Output:

32
Example (GetAge)
now, err := time.Parse(time.DateOnly, "2023-09-01")
if err != nil {
	panic(err)
}

fmt.Println(GetAge(now)(MakeUser("2005-12-12")))
fmt.Println(GetAge(now)(MakeUser("July 4, 2001")))

fortune := F.Flow3(
	N.Add(365.0),
	S.Format[float64]("%0.0f"),
	Concat("If you survive, you will be "),
)

zoltar := F.Flow3(
	GetAge(now),
	E.Map[error](fortune),
	E.GetOrElse(errors.ToString),
)

fmt.Println(zoltar(MakeUser("2005-12-12")))
Output:

Right[float64](6472)
Left[*time.ParseError](parsing time "July 4, 2001" as "2006-01-02": cannot parse "July 4, 2001" as "2006")
If you survive, you will be 6837
Example (Greeting)
package main

import "fmt"

func Hi(name string) string {
	return fmt.Sprintf("Hi %s", name)
}

func Greeting(name string) string {
	return Hi(name)
}

func main() {
	// functions are first class objects
	greet := Hi

	fmt.Println(Greeting("times"))
	fmt.Println(greet("times"))

}
Output:

Hi times
Hi times
Example (Pipe)
output := F.Pipe2(
	"send in the clowns",
	ToUpper,
	Exclaim,
)

fmt.Println(output)
Output:

SEND IN THE CLOWNS!
Example (RenderPage)
// prepare the http client
client := H.MakeClient(http.DefaultClient)

// get returns the title of the nth item from the REST service
get := F.Flow4(
	idxToURL,
	H.MakeGetRequest,
	H.ReadJSON[PostItem](client),
	R.Map(PostItem.getTitle),
)

res := F.Pipe2(
	R.Of(renderString),                // start with a function with 2 unresolved arguments
	R.Ap[func(string) string](get(1)), // resolve the first argument
	R.Ap[string](get(2)),              // in parallel resolve the second argument
)

// finally invoke in context and start
fmt.Println(res(context.TODO())())
Output:

Right[string](<div>Destinations: [qui est esse], Events: [ea molestias quasi exercitationem repellat qui ipsa sit aut]</div>)
Example (Shout)
fmt.Println(Shout("send in the clowns"))
Output:

SEND IN THE CLOWNS!
Example (Solution04A)
// words :: String -> [String]
words := Split(regexp.MustCompile(` `))

fmt.Println(words("Jingle bells Batman smells"))
Output:

[Jingle bells Batman smells]
Example (Solution04B)
// filterQs :: [String] -> [String]
filterQs := A.Filter(Matches(regexp.MustCompile(`q`)))

fmt.Println(filterQs(A.From("quick", "camels", "quarry", "over", "quails")))
Output:

[quick quarry quails]
Example (Solution04C)
keepHighest := N.Max[int]

// max :: [Number] -> Number
max := A.Reduce(keepHighest, math.MinInt)

fmt.Println(max(A.From(323, 523, 554, 123, 5234)))
Output:

5234
Example (Solution05A)
IsLastInStock := F.Flow2(
	A.Last[Car],
	O.Map(Car.getInStock),
)

fmt.Println(IsLastInStock(Cars[0:3]))
fmt.Println(IsLastInStock(Cars[3:]))
Output:

Some[bool](true)
Some[bool](false)
Example (Solution05B)
// averageDollarValue :: [Car] -> Int
averageDollarValue := F.Flow2(
	A.Map(Car.getDollarValue),
	average,
)

fmt.Println(averageDollarValue(Cars))
Output:

790700
Example (Solution05C)
// order by horsepower
ordByHorsepower := ord.Contramap(Car.getHorsepower)(I.Ord)

// fastestCar :: [Car] -> Option[String]
fastestCar := F.Flow3(
	A.Sort(ordByHorsepower),
	A.Last[Car],
	O.Map(F.Flow2(
		Car.getName,
		S.Format[string]("%s is the fastest"),
	)),
)

fmt.Println(fastestCar(Cars))
Output:

Some[string](Aston Martin One-77 is the fastest)
Example (Solution08A)
incrF := I.Map(N.Add(1))

fmt.Println(incrF(I.Of(2)))
Output:

3
Example (Solution08B)
// initial :: User -> Option rune
initial := F.Flow3(
	Chapter08User.getName,
	S.ToRunes,
	A.Head[rune],
)

fmt.Println(initial(albert08))
Output:

Some[int32](65)
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](your account is not active)
Right[string](Welcome Theresa)
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](Your name Yi is larger than 3 characters)
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]
Example (Solution09B)
logFilename := F.Flow2(
	io.Map(path.Base),
	io.ChainFirst(pureLog),
)

fmt.Println(logFilename(getFile)())
Output:

ch09.md
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](email notanemail is invalid)
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]
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]
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)
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]
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[string](Albert)
Right[string](Gary)
Right[string](Theresa)
Left[*errors.errorString](user 4 not found)
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
Example (Solution12A)
// getJsons :: Map Route Route -> Task Error (Map Route JSON)
getJsons := IOE.TraverseRecord[string](httpGet)

fmt.Println(getJsons(routes)())
Output:

Right[map[string]string](map[/:json for / /about:json for /about])
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[string](Game started)
Left[*errors.errorString](player 4 must have a name)
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[option.Option[string]](Some[string](content of file1 (utf-8)))
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})
Example (Widthdraw)
fmt.Println(getTwenty(MakeAccount(200)))
fmt.Println(getTwenty(MakeAccount(10)))
Output:

Your balance is $180.00
You're broke!

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	Cars = A.From(Car{
		Name:        "Ferrari FF",
		Horsepower:  660,
		DollarValue: 700000,
		InStock:     true,
	}, Car{
		Name:        "Spyker C12 Zagato",
		Horsepower:  650,
		DollarValue: 648000,
		InStock:     false,
	}, Car{
		Name:        "Jaguar XKR-S",
		Horsepower:  550,
		DollarValue: 132000,
		InStock:     true,
	}, Car{
		Name:        "Audi R8",
		Horsepower:  525,
		DollarValue: 114200,
		InStock:     false,
	}, Car{
		Name:        "Aston Martin One-77",
		Horsepower:  750,
		DollarValue: 1850000,
		InStock:     true,
	}, Car{
		Name:        "Pagani Huayra",
		Horsepower:  700,
		DollarValue: 1300000,
		InStock:     false,
	})
)

Functions

This section is empty.

Types

type Car

type Car struct {
	Name        string
	Horsepower  int
	DollarValue float32
	InStock     bool
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL