diff --git a/number/magma.go b/number/magma.go index 4848e3e..775d533 100644 --- a/number/magma.go +++ b/number/magma.go @@ -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 + }) +} diff --git a/number/monoid.go b/number/monoid.go index c9f32d0..25d3607 100644 --- a/number/monoid.go +++ b/number/monoid.go @@ -19,6 +19,7 @@ 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( @@ -26,3 +27,12 @@ func MonoidSum[A Number]() M.Monoid[A] { 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, + ) +} diff --git a/number/semigroup.go b/number/semigroup.go index e3733e5..485a194 100644 --- a/number/semigroup.go +++ b/number/semigroup.go @@ -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 + }) +} diff --git a/number/utils.go b/number/utils.go index e8ed5c5..7744faa 100644 --- a/number/utils.go +++ b/number/utils.go @@ -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 } } diff --git a/samples/mostly-adequate/chapter02_firstclassfunctions_test.go b/samples/mostly-adequate/chapter02_firstclassfunctions_test.go new file mode 100644 index 0000000..9b3ab7e --- /dev/null +++ b/samples/mostly-adequate/chapter02_firstclassfunctions_test.go @@ -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 +} diff --git a/samples/mostly-adequate/chapter04_currying_test.go b/samples/mostly-adequate/chapter04_currying_test.go index df0f877..c57b187 100644 --- a/samples/mostly-adequate/chapter04_currying_test.go +++ b/samples/mostly-adequate/chapter04_currying_test.go @@ -16,9 +16,12 @@ 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" @@ -26,8 +29,9 @@ import ( ) 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 @@ -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 +} diff --git a/samples/mostly-adequate/chapter05_composing_test.go b/samples/mostly-adequate/chapter05_composing_test.go index df63b76..be42890 100644 --- a/samples/mostly-adequate/chapter05_composing_test.go +++ b/samples/mostly-adequate/chapter05_composing_test.go @@ -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" ) @@ -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) +} diff --git a/samples/mostly-adequate/support.go b/samples/mostly-adequate/support.go new file mode 100644 index 0000000..2fb9907 --- /dev/null +++ b/samples/mostly-adequate/support.go @@ -0,0 +1,89 @@ +// 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 ( + A "github.com/IBM/fp-go/array" + F "github.com/IBM/fp-go/function" + N "github.com/IBM/fp-go/number" +) + +type ( + Car struct { + Name string + Horsepower int + DollarValue float32 + InStock bool + } +) + +func (car Car) getInStock() bool { + return car.InStock +} + +func (car Car) getDollarValue() float32 { + return car.DollarValue +} + +func (car Car) getHorsepower() int { + return car.Horsepower +} + +func (car Car) getName() string { + return car.Name +} + +func average(val []float32) float32 { + return F.Pipe2( + val, + A.Fold(N.MonoidSum[float32]()), + N.Div(float32(len(val))), + ) +} + +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, + }) +)