mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
fix: add examples for [Frisby's Mostly Adequate Guide]
This commit is contained in:
23
number/integer/string.go
Normal file
23
number/integer/string.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// 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 integer
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ToString converts an integer to a string
|
||||||
|
ToString = strconv.Itoa
|
||||||
|
)
|
14
ord/ord.go
14
ord/ord.go
@@ -131,9 +131,7 @@ func FromStrictCompare[A C.Ordered]() Ord[A] {
|
|||||||
return MakeOrd(strictCompare[A], strictEq[A])
|
return MakeOrd(strictCompare[A], strictEq[A])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Lt tests whether one value is _strictly less than_ another
|
||||||
* Test whether one value is _strictly less than_ another
|
|
||||||
*/
|
|
||||||
func Lt[A any](O Ord[A]) func(A) func(A) bool {
|
func Lt[A any](O Ord[A]) func(A) func(A) bool {
|
||||||
return func(second A) func(A) bool {
|
return func(second A) func(A) bool {
|
||||||
return func(first A) bool {
|
return func(first A) bool {
|
||||||
@@ -142,9 +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
|
||||||
* Test whether one value is less or equal than_ another
|
|
||||||
*/
|
|
||||||
func Leq[A any](O Ord[A]) func(A) func(A) bool {
|
func Leq[A any](O Ord[A]) func(A) func(A) bool {
|
||||||
return func(second A) func(A) bool {
|
return func(second A) func(A) bool {
|
||||||
return func(first A) bool {
|
return func(first A) bool {
|
||||||
@@ -156,7 +152,7 @@ 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 Gt[A any](O Ord[A]) func(A) func(A) bool {
|
func cc[A any](O Ord[A]) func(A) func(A) bool {
|
||||||
return func(second A) func(A) bool {
|
return func(second A) func(A) bool {
|
||||||
return func(first A) bool {
|
return func(first A) bool {
|
||||||
return O.Compare(first, second) > 0
|
return O.Compare(first, second) > 0
|
||||||
@@ -164,9 +160,7 @@ func Gt[A any](O Ord[A]) func(A) func(A) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Geq tests whether one value is greater or equal than_ another
|
||||||
* Test whether one value is greater or equal than_ another
|
|
||||||
*/
|
|
||||||
func Geq[A any](O Ord[A]) func(A) func(A) bool {
|
func Geq[A any](O Ord[A]) func(A) func(A) bool {
|
||||||
return func(second A) func(A) bool {
|
return func(second A) func(A) bool {
|
||||||
return func(first A) bool {
|
return func(first A) bool {
|
||||||
|
6
samples/mostly-adequate/README.md
Normal file
6
samples/mostly-adequate/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Mostly Adequate: fp-go Companion Guide
|
||||||
|
|
||||||
|
This resource is meant to serve as a go "companion" resource to Professor [Frisby's Mostly Adequate Guide](https://github.com/MostlyAdequate/mostly-adequate-guide).
|
||||||
|
|
||||||
|
It is a port of the [mostly-adequate-fp-ts](https://github.com/ChuckJonas/mostly-adequate-fp-ts/) book.
|
||||||
|
|
47
samples/mostly-adequate/chapter01_test.go
Normal file
47
samples/mostly-adequate/chapter01_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
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 Example_flock() {
|
||||||
|
|
||||||
|
flockA := MakeFlock(4)
|
||||||
|
flockB := MakeFlock(2)
|
||||||
|
flockC := MakeFlock(0)
|
||||||
|
|
||||||
|
fmt.Println(flockA.Conjoin(&flockC).Breed(&flockB).Conjoin(flockA.Breed(&flockB)).Seagulls)
|
||||||
|
|
||||||
|
// Output: 32
|
||||||
|
}
|
46
samples/mostly-adequate/chapter04_currying_test.go
Normal file
46
samples/mostly-adequate/chapter04_currying_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// 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 (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
N "github.com/IBM/fp-go/number"
|
||||||
|
I "github.com/IBM/fp-go/number/integer"
|
||||||
|
S "github.com/IBM/fp-go/string"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Match = F.Curry2((*regexp.Regexp).FindStringSubmatch)
|
||||||
|
Split = F.Curry2(F.Bind3of3((*regexp.Regexp).Split)(-1))
|
||||||
|
|
||||||
|
Add = N.Add[int]
|
||||||
|
ToString = I.ToString
|
||||||
|
ToLower = strings.ToLower
|
||||||
|
ToUpper = strings.ToUpper
|
||||||
|
Concat = F.Curry2(S.Monoid.Concat)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace cannot be generated via [F.Curry3] because the order of parameters does not match our desired curried order
|
||||||
|
func Replace(search *regexp.Regexp) func(replace string) func(s string) string {
|
||||||
|
return func(replace string) func(s string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return search.ReplaceAllString(s, replace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
samples/mostly-adequate/chapter05_composing_test.go
Normal file
60
samples/mostly-adequate/chapter05_composing_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
S "github.com/IBM/fp-go/string"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Exclaim = S.Format[string]("%s!")
|
||||||
|
Shout = F.Flow2(ToUpper, Exclaim)
|
||||||
|
Dasherize = F.Flow4(
|
||||||
|
Replace(regexp.MustCompile(`\s{2,}`))(" "),
|
||||||
|
Split(regexp.MustCompile(` `)),
|
||||||
|
A.Map(ToLower),
|
||||||
|
A.Intercalate(S.Monoid)("-"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_shout() {
|
||||||
|
fmt.Println(Shout("send in the clowns"))
|
||||||
|
|
||||||
|
// Output: SEND IN THE CLOWNS!
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_dasherize() {
|
||||||
|
fmt.Println(Dasherize("The world is a vampire"))
|
||||||
|
|
||||||
|
// Output: the-world-is-a-vampire
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_pipe() {
|
||||||
|
output := F.Pipe2(
|
||||||
|
"send in the clowns",
|
||||||
|
ToUpper,
|
||||||
|
Exclaim,
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(output)
|
||||||
|
|
||||||
|
// Output: SEND IN THE CLOWNS!
|
||||||
|
}
|
133
samples/mostly-adequate/chapter08_tupperware_test.go
Normal file
133
samples/mostly-adequate/chapter08_tupperware_test.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// 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"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
"github.com/IBM/fp-go/errors"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
N "github.com/IBM/fp-go/number"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
"github.com/IBM/fp-go/ord"
|
||||||
|
S "github.com/IBM/fp-go/string"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Balance float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeAccount(b float32) Account {
|
||||||
|
return Account{Balance: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBalance(a Account) float32 {
|
||||||
|
return a.Balance
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ordFloat32 = ord.FromStrictCompare[float32]()
|
||||||
|
UpdateLedger = F.Identity[Account]
|
||||||
|
RemainingBalance = F.Flow2(
|
||||||
|
getBalance,
|
||||||
|
S.Format[float32]("Your balance is $%0.2f"),
|
||||||
|
)
|
||||||
|
FinishTransaction = F.Flow2(
|
||||||
|
UpdateLedger,
|
||||||
|
RemainingBalance,
|
||||||
|
)
|
||||||
|
getTwenty = F.Flow2(
|
||||||
|
Withdraw(20),
|
||||||
|
O.Fold(F.Constant("You're broke!"), FinishTransaction),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Withdraw(amount float32) func(account Account) O.Option[Account] {
|
||||||
|
|
||||||
|
return F.Flow3(
|
||||||
|
getBalance,
|
||||||
|
O.FromPredicate(ord.Geq(ordFloat32)(amount)),
|
||||||
|
O.Map(F.Flow2(
|
||||||
|
N.Add(-amount),
|
||||||
|
MakeAccount,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
BirthDate string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBirthDate(u User) string {
|
||||||
|
return u.BirthDate
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeUser(d string) User {
|
||||||
|
return User{BirthDate: d}
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseDate = F.Bind1of2(E.Eitherize2(time.Parse))(time.DateOnly)
|
||||||
|
|
||||||
|
func GetAge(now time.Time) func(User) E.Either[error, float64] {
|
||||||
|
return F.Flow3(
|
||||||
|
getBirthDate,
|
||||||
|
parseDate,
|
||||||
|
E.Map[error](F.Flow3(
|
||||||
|
now.Sub,
|
||||||
|
time.Duration.Hours,
|
||||||
|
N.Mul(1/24.0),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_widthdraw() {
|
||||||
|
fmt.Println(getTwenty(MakeAccount(200)))
|
||||||
|
fmt.Println(getTwenty(MakeAccount(10)))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Your balance is $180.00
|
||||||
|
// You're broke!
|
||||||
|
}
|
||||||
|
|
||||||
|
func 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[<nil>, float64](6472)
|
||||||
|
// 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
|
||||||
|
}
|
64
samples/mostly-adequate/chapter09_monadiconions_test.go
Normal file
64
samples/mostly-adequate/chapter09_monadiconions_test.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// 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"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Street struct {
|
||||||
|
Name string
|
||||||
|
Number int
|
||||||
|
}
|
||||||
|
|
||||||
|
Address struct {
|
||||||
|
Street Street
|
||||||
|
Postcode string
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressBook struct {
|
||||||
|
Addresses []Address
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAddresses(ab AddressBook) []Address {
|
||||||
|
return ab.Addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStreet(s Address) Street {
|
||||||
|
return s.Street
|
||||||
|
}
|
||||||
|
|
||||||
|
var FirstAddressStreet = F.Flow3(
|
||||||
|
getAddresses,
|
||||||
|
A.Head[Address],
|
||||||
|
O.Map(getStreet),
|
||||||
|
)
|
||||||
|
|
||||||
|
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})
|
||||||
|
}
|
19
samples/mostly-adequate/doc.go
Normal file
19
samples/mostly-adequate/doc.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// 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 is meant to serve as a go "companion" resource to Professor [Frisby's Mostly Adequate Guide].
|
||||||
|
//
|
||||||
|
// [Frisby's Mostly Adequate Guide]: https://github.com/MostlyAdequate/mostly-adequate-guide
|
||||||
|
package mostlyadequate
|
Reference in New Issue
Block a user