Skip to content

Commit

Permalink
Merge pull request #35 from IBM/cleue-mostly-adequate-capter10
Browse files Browse the repository at this point in the history
Add more examples for mostly-adequate
  • Loading branch information
CarstenLeue authored Sep 6, 2023
2 parents 29d9882 + 52b71ef commit 3ccafb5
Show file tree
Hide file tree
Showing 14 changed files with 691 additions and 21 deletions.
6 changes: 6 additions & 0 deletions number/magma.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ func MagmaSub[A Number]() M.Magma[A] {
return first - second
})
}

func MagmaDiv[A Number]() M.Magma[A] {
return M.MakeMagma(func(first A, second A) A {
return first / second
})
}
10 changes: 10 additions & 0 deletions number/monoid.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,20 @@ import (
M "github.com/IBM/fp-go/monoid"
)

// MonoidSum is the [Monoid] that adds elements with a zero empty element
func MonoidSum[A Number]() M.Monoid[A] {
s := SemigroupSum[A]()
return M.MakeMonoid(
s.Concat,
0,
)
}

// MonoidProduct is the [Monoid] that multiplies elements with a one empty element
func MonoidProduct[A Number]() M.Monoid[A] {
s := SemigroupProduct[A]()
return M.MakeMonoid(
s.Concat,
1,
)
}
6 changes: 6 additions & 0 deletions number/semigroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ func SemigroupSum[A Number]() S.Semigroup[A] {
return first + second
})
}

func SemigroupProduct[A Number]() S.Semigroup[A] {
return S.MakeSemigroup(func(first A, second A) A {
return first * second
})
}
26 changes: 20 additions & 6 deletions number/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,30 @@ type Number interface {
}

// Add is a curried function used to add two numbers
func Add[T Number](left T) func(T) T {
return func(right T) T {
func Add[T Number](right T) func(T) T {
return func(left T) T {
return left + right
}
}

// Mul is a curried function used to add two numbers
func Mul[T Number](coeff T) func(T) T {
return func(value T) T {
return coeff * value
// Sub is a curried function used to subtract two numbers
func Sub[T Number](right T) func(T) T {
return func(left T) T {
return left - right
}
}

// Mul is a curried function used to multiply two numbers
func Mul[T Number](right T) func(T) T {
return func(left T) T {
return left * right
}
}

// Div is a curried function used to divide two numbers
func Div[T Number](right T) func(T) T {
return func(left T) T {
return left / right
}
}

Expand Down
10 changes: 5 additions & 5 deletions ord/ord.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -150,17 +150,17 @@ 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
}
}
}

// 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 {
Expand Down
38 changes: 38 additions & 0 deletions samples/mostly-adequate/chapter02_firstclassfunctions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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"

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

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

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

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

// Output:
// Hi times
// Hi times
}
41 changes: 39 additions & 2 deletions samples/mostly-adequate/chapter04_currying_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,22 @@
package mostlyadequate

import (
"fmt"
"math"
"regexp"
"strings"

A "github.com/IBM/fp-go/array"
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))
Match = F.Curry2((*regexp.Regexp).FindStringSubmatch)
Matches = F.Curry2((*regexp.Regexp).MatchString)
Split = F.Curry2(F.Bind3of3((*regexp.Regexp).Split)(-1))

Add = N.Add[int]
ToString = I.ToString
Expand All @@ -44,3 +48,36 @@ func Replace(search *regexp.Regexp) func(replace string) func(s string) string {
}
}
}

func Example_solution04A() {
// words :: String -> [String]
words := Split(regexp.MustCompile(` `))

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

// Output:
// [Jingle bells Batman smells]
}

func 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]
}

func 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
}
50 changes: 50 additions & 0 deletions samples/mostly-adequate/chapter05_composing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (

A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
I "github.com/IBM/fp-go/number/integer"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/ord"
S "github.com/IBM/fp-go/string"
)

Expand Down Expand Up @@ -58,3 +61,50 @@ func Example_pipe() {

// Output: SEND IN THE CLOWNS!
}

func 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)
}

func Example_solution05B() {
// averageDollarValue :: [Car] -> Int
averageDollarValue := F.Flow2(
A.Map(Car.getDollarValue),
average,
)

fmt.Println(averageDollarValue(Cars))

// Output:
// 790700
}

func 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)
}
115 changes: 115 additions & 0 deletions samples/mostly-adequate/chapter06_exampleapplication_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// 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 (
"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 Example_application() {
// 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

}
Loading

0 comments on commit 3ccafb5

Please sign in to comment.