From 5d8773bdd1d9f95e59d1c8865ae8701c17329216 Mon Sep 17 00:00:00 2001 From: medmouine Date: Thu, 8 Dec 2022 10:50:52 -0500 Subject: [PATCH 1/2] Add primitive interfaces --- fold/fold.go | 6 ++-- fold/fold_test.go | 10 +++---- identity/identity.go | 21 ++++++++++++-- identity/identity_test.go | 29 +++++++++++++++++++ io/io.go | 41 ++++++++------------------- io/io_test.go | 59 +++++++++++++++------------------------ magma/magma.go | 19 +++++++++++++ magma/magma_test.go | 48 +++++++++++++++++++++++++++++++ 8 files changed, 157 insertions(+), 76 deletions(-) diff --git a/fold/fold.go b/fold/fold.go index 04b22d7..2a082ce 100644 --- a/fold/fold.go +++ b/fold/fold.go @@ -1,6 +1,6 @@ -package collection_utils +package fold -func FoldLeft[T any](coll []T, op func(T, T) T, initial T) T { +func Left[T any](coll []T, op func(T, T) T, initial T) T { for _, x := range coll { initial = op(initial, x) } @@ -9,7 +9,7 @@ func FoldLeft[T any](coll []T, op func(T, T) T, initial T) T { return initial } -func FoldRight[T any](coll []T, op func(T, T) T, initial T) T { +func Right[T any](coll []T, op func(T, T) T, initial T) T { for i := len(coll) - 1; i >= 0; i-- { initial = op(coll[i], initial) } diff --git a/fold/fold_test.go b/fold/fold_test.go index a71eda8..8272f0d 100644 --- a/fold/fold_test.go +++ b/fold/fold_test.go @@ -1,4 +1,4 @@ -package collection_utils +package fold import "testing" @@ -35,9 +35,9 @@ func TestFoldLeft(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - got := FoldLeft(tc.coll, op, tc.x) + got := Left(tc.coll, op, tc.x) if got != tc.want { - t.Errorf("FoldLeft(%v, %v, %v) = %v; want %v", tc.coll, "op", tc.x, got, tc.want) + t.Errorf("Left(%v, %v, %v) = %v; want %v", tc.coll, "op", tc.x, got, tc.want) } }) } @@ -76,9 +76,9 @@ func TestFoldRight(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - got := FoldRight(tc.coll, op, tc.x) + got := Right(tc.coll, op, tc.x) if got != tc.want { - t.Errorf("FoldRight(%v, %v, %v) = %v; want %v", tc.coll, "op", tc.x, got, tc.want) + t.Errorf("Right(%v, %v, %v) = %v; want %v", tc.coll, "op", tc.x, got, tc.want) } }) } diff --git a/identity/identity.go b/identity/identity.go index 002b077..4c19d09 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -1,5 +1,22 @@ package identity -func Identity[T any](i T) T { - return i +type Identity[T any] interface { + Bind(func(T) Identity[T]) Identity[T] + Unwrap() T +} + +type identity[T any] struct { + v T +} + +func Of[T any](v T) Identity[T] { + return &identity[T]{v} +} + +func (i *identity[T]) Bind(f func(T) Identity[T]) Identity[T] { + return f(i.v) +} + +func (i *identity[T]) Unwrap() T { + return i.v } diff --git a/identity/identity_test.go b/identity/identity_test.go index cca56a0..1f352ce 100644 --- a/identity/identity_test.go +++ b/identity/identity_test.go @@ -1 +1,30 @@ package identity + +import "testing" + +func TestOf(t *testing.T) { + identity := Of[string]("hello") + result := identity.Unwrap() + if result != "hello" { + t.Errorf("Expected 'hello', got '%s'", result) + } +} + +func TestBind(t *testing.T) { + identity1 := Of[string]("hello") + identity2 := identity1.Bind(func(s string) Identity[string] { + return Of[string](s + " world") + }) + result := identity2.Unwrap() + if result != "hello world" { + t.Errorf("Expected 'hello world', got '%s'", result) + } +} + +func TestValue(t *testing.T) { + identity := Of[string]("hello") + result := identity.Unwrap() + if result != "hello" { + t.Errorf("Expected 'hello', got '%s'", result) + } +} diff --git a/io/io.go b/io/io.go index 8beba81..7ba16d8 100644 --- a/io/io.go +++ b/io/io.go @@ -1,43 +1,24 @@ package io type IO[T any] interface { - Call() T - Map(f func(T) T) IO[T] + Run() T + Bind(func(T) IO[T]) IO[T] } type io[T any] struct { - IO[T] - f func() T + v T + f func() } -func Of[T any](t T) IO[T] { - return io[T]{ - f: func() T { - return t - }, - } +func Of[T any](v T, f func()) IO[T] { + return &io[T]{v: v, f: f} } -func From[T any](f func() T) IO[T] { - return io[T]{ - f: f, - } +func (io io[T]) Run() T { + io.f() + return io.v } -func Map[T any, U any](io IO[T], fn func(T) U) IO[U] { - f := func() U { - return fn(io.Call()) - } - return From(f) -} - -func (i io[T]) Call() T { - return i.f() -} - -func (i io[T]) Map(fn func(T) T) IO[T] { - f := func() T { - return fn(i.Call()) - } - return From(f) +func (io io[T]) Bind(f func(T) IO[T]) IO[T] { + return f(io.v) } diff --git a/io/io_test.go b/io/io_test.go index 0355053..d00371d 100644 --- a/io/io_test.go +++ b/io/io_test.go @@ -1,53 +1,40 @@ package io import ( - "reflect" "testing" ) func TestOf(t *testing.T) { - got := Of("foo") - - if !reflect.DeepEqual(got.Call(), "foo") { - t.Errorf("Of(\"foo\") = %v, want %v", got.Call(), "foo") + var printed bool + io := Of[string]("hello", func() { printed = true }) + result := io.Run() + if result != "hello" { + t.Errorf("Expected 'hello', got '%s'", result) } -} - -func TestFrom(t *testing.T) { - fn := func() string { - return "foo" - } - got := From(fn) - - if !reflect.DeepEqual(got.Call(), "foo") { - t.Errorf("From() = %v, want %v", got, "foo") + if !printed { + t.Error("Expected side effect to be executed") } } -func TestMap(t *testing.T) { - fn1 := func() string { - return "foo" - } - fn2 := func(s string) int { - return len(s) - } - got := Map(From(fn1), fn2) - - if !reflect.DeepEqual(got.Call(), 3) { - t.Errorf("Map() = %v, want %v", got.Call(), 3) +func TestBind(t *testing.T) { + io1 := Of[string]("hello", func() {}) + io2 := io1.Bind(func(s string) IO[string] { + return Of[string](s+" world", func() {}) + }) + result := io2.Run() + if result != "hello world" { + t.Errorf("Expected 'hello world', got '%s'", result) } } -func Test_io_Map(t *testing.T) { - fn1 := func() string { - return "foo" +func TestRun(t *testing.T) { + var printed bool + io := Of[string]("hello", func() { printed = true }) + result := io.Run() + if result != "hello" { + t.Errorf("Expected 'hello', got '%s'", result) } - fn2 := func(s string) string { - return s + "bar" - } - got := From(fn1).Map(fn2) - - if !reflect.DeepEqual(got.Call(), "foobar") { - t.Errorf("Map() = %v, want %v", got.Call(), "foobar") + if !printed { + t.Error("Expected side effect to be executed") } } diff --git a/magma/magma.go b/magma/magma.go index 8aee5ff..779c678 100644 --- a/magma/magma.go +++ b/magma/magma.go @@ -1 +1,20 @@ package magma + +import "github.com/medmouine/gomad/fold" + +type Magma[T any] interface { + Concat(T) T +} + +type magma[T any] struct { + values []T + op func(T, T) T +} + +func Of[T any](values []T, op func(T, T) T) Magma[T] { + return &magma[T]{values: values, op: op} +} + +func (m magma[T]) Concat(x T) T { + return fold.Left(m.values, m.op, x) +} diff --git a/magma/magma_test.go b/magma/magma_test.go index 8aee5ff..59f12ee 100644 --- a/magma/magma_test.go +++ b/magma/magma_test.go @@ -1 +1,49 @@ package magma + +import "testing" + +func TestConcat(t *testing.T) { + op := func(x, y int) int { + return x + y + } + + testCases := []struct { + desc string + values []int + x int + want int + }{ + { + desc: "empty set", + values: []int{}, + x: 0, + want: 0, + }, + { + desc: "single-element set", + values: []int{1}, + x: 0, + want: 1, + }, + { + desc: "multi-element set", + values: []int{1, 2, 3, 4, 5}, + x: 0, + want: 15, + }, + } + + // run the tests + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // create a Magma instance + mg := Of(tc.values, op) + + // test the Concat method + got := mg.Concat(tc.x) + if got != tc.want { + t.Errorf("Concat(%v) = %v; want %v", tc.x, got, tc.want) + } + }) + } +} From 301eccb82c81c65617695c94a338f8cc9e286b32 Mon Sep 17 00:00:00 2001 From: medmouine Date: Thu, 8 Dec 2022 10:58:56 -0500 Subject: [PATCH 2/2] fix functor --- HTK/HKT.go | 4 +++ functor/functor.go | 28 +++-------------- functor/functor_test.go | 68 ----------------------------------------- 3 files changed, 9 insertions(+), 91 deletions(-) delete mode 100644 functor/functor_test.go diff --git a/HTK/HKT.go b/HTK/HKT.go index f32eed2..ad83ff6 100644 --- a/HTK/HKT.go +++ b/HTK/HKT.go @@ -1 +1,5 @@ package HTK + +type HKT[T, U any] interface { + Unwrap() T +} diff --git a/functor/functor.go b/functor/functor.go index 6711982..cde6f33 100644 --- a/functor/functor.go +++ b/functor/functor.go @@ -1,29 +1,11 @@ package functor -type IFunctor[T any] interface { - Map(T) T -} - -type Functor[T any] struct { - Val T -} - -func Lift[T any, U any](f func(T) U) func(Functor[T]) Functor[U] { - return func(fa Functor[T]) Functor[U] { - return Functor[U]{ - Val: f(fa.Val), - } - } -} +import "github.com/medmouine/gomad/HTK" -func Map[T any, U any](fa Functor[T], f func(T) U) Functor[U] { - return Functor[U]{ - Val: f(fa.Val), - } +type Functor[T, U any] interface { + HTK.HKT[T, U] } -func (fa Functor[T]) Map(f func(T) T) Functor[T] { - return Functor[T]{ - Val: f(fa.Val), - } +type functor[T, U any] struct { + Functor[T, U] } diff --git a/functor/functor_test.go b/functor/functor_test.go deleted file mode 100644 index 7f54a83..0000000 --- a/functor/functor_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package functor_test - -import ( - "strconv" - "testing" - - . "github.com/medmouine/gomad/functor" - "github.com/medmouine/gomad/identity" -) - -func TestLift(t *testing.T) { - t.Parallel() - - intToString := strconv.Itoa - got := Lift(intToString) - - intFunctor := Functor[int]{ - Val: 1, - } - - if got(intFunctor).Val != "1" { - t.Errorf("Lift(intToString) failed") - } - - got2 := Lift(identity.Identity[int]) - - if got2(intFunctor).Val != 1 { - t.Errorf("Lift(identity.Identity) failed") - } -} - -func TestMap(t *testing.T) { - t.Parallel() - - intToString := strconv.Itoa - - intFunctor := Functor[int]{ - Val: 1, - } - - got := Map(intFunctor, intToString) - - if got.Val != "1" { - t.Errorf("Map(intToString) failed") - } - - got2 := Map(intFunctor, identity.Identity[int]) - - if got2.Val != 1 { - t.Errorf("Map(identity.Identity) failed") - } -} - -func TestFunctor_Map(t *testing.T) { - t.Parallel() - - intFunctor := Functor[int]{ - Val: 1, - } - - got := intFunctor.Map(func(i int) int { - return 5 - }) - - if got.Val != 5 { - t.Errorf("Map(intToString) failed") - } -}