Skip to content

Commit

Permalink
Merge pull request #33 from IBM/cleue-mostly-adequate-fp-go
Browse files Browse the repository at this point in the history
fix: add examples for [Frisby's Mostly Adequate Guide]
  • Loading branch information
CarstenLeue authored Sep 1, 2023
2 parents ce66cf2 + fb3b1f1 commit 8650a8a
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 10 deletions.
23 changes: 23 additions & 0 deletions number/integer/string.go
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 10 deletions ord/ord.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ func FromStrictCompare[A C.Ordered]() Ord[A] {
return MakeOrd(strictCompare[A], strictEq[A])
}

/**
* Test 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 {
Expand All @@ -142,9 +140,7 @@ func Lt[A any](O Ord[A]) func(A) func(A) bool {
}
}

/**
* Test 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 {
Expand All @@ -156,17 +152,15 @@ func Leq[A any](O Ord[A]) func(A) func(A) bool {
/**
* 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(first A) bool {
return O.Compare(first, second) > 0
}
}
}

/**
* Test 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 {
Expand Down
6 changes: 6 additions & 0 deletions samples/mostly-adequate/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 47 additions & 0 deletions samples/mostly-adequate/chapter01_test.go
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions samples/mostly-adequate/chapter04_currying_test.go
Original file line number Diff line number Diff line change
@@ -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 changes: 60 additions & 0 deletions samples/mostly-adequate/chapter05_composing_test.go
Original file line number Diff line number Diff line change
@@ -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 changes: 133 additions & 0 deletions samples/mostly-adequate/chapter08_tupperware_test.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 8650a8a

Please sign in to comment.