From a8acecad001ca5e1464dcefe64b76077cd5e9695 Mon Sep 17 00:00:00 2001 From: lixiaohui Date: Sun, 15 May 2022 16:27:21 +0800 Subject: [PATCH] [fp] add fp functions & curry & compose & state --- fp/compose.go | 26 ++++++++ fp/fn1.go | 29 +++++++++ fp/fn2.go | 31 ++++++++++ fp/fn3.go | 33 ++++++++++ fp/fn4.go | 35 +++++++++++ fp/fn5.go | 37 +++++++++++ fp/fp_test.go | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++ fp/state.go | 17 +++++ 8 files changed, 375 insertions(+) create mode 100644 fp/compose.go create mode 100644 fp/fn1.go create mode 100644 fp/fn2.go create mode 100644 fp/fn3.go create mode 100644 fp/fn4.go create mode 100644 fp/fn5.go create mode 100644 fp/fp_test.go create mode 100644 fp/state.go diff --git a/fp/compose.go b/fp/compose.go new file mode 100644 index 0000000..885388e --- /dev/null +++ b/fp/compose.go @@ -0,0 +1,26 @@ +package fp + +// Compose1 is a function that compose one function. +func Compose1[A, R any](f func(A) R) func(A) R { + return func(a A) R { return f(a) } +} + +// Compose2 is a function that compose two functions. +func Compose2[A, B, R any](f func(A) B, g func(B) R) func(A) R { + return func(x A) R { return g(f(x)) } +} + +// Compose3 is a function that compose three functions. +func Compose3[A, B, C, R any](f func(A) B, g func(B) C, h func(C) R) func(A) R { + return func(x A) R { return h(g(f(x))) } +} + +// Compose4 is a function that compose four functions. +func Compose4[A, B, C, D, R any](f func(A) B, g func(B) C, h func(C) D, i func(D) R) func(A) R { + return func(x A) R { return i(h(g(f(x)))) } +} + +// Compose5 is a function that compose five functions. +func Compose5[A, B, C, D, E, R any](f func(A) B, g func(B) C, h func(C) D, i func(D) E, j func(E) R) func(A) R { + return func(x A) R { return j(i(h(g(f(x))))) } +} diff --git a/fp/fn1.go b/fp/fn1.go new file mode 100644 index 0000000..dd8f2e2 --- /dev/null +++ b/fp/fn1.go @@ -0,0 +1,29 @@ +package fp + +// Function1 is a function that takes one argument. +type Function1[A, R any] interface { + Apply(A) R + Curry() func(A) R +} + +// Fn1 is a function that takes one argument. +type Fn1[A, R any] func(A) R + +func (f Fn1[A, R]) Apply(a A) R { return f(a) } + +func (f Fn1[A, R]) Curry() func(A) R { return Fn1[A, R](func(a A) R { return f.Apply(a) }) } + +// Apply1 is a function that apply one argument to a function. +func Apply1[A, R any](f func(A) R, a A) R { + return Fn1[A, R](f).Apply(a) +} + +// Curry1 is a function that curry one argument function. +func Curry1[A, R any](f func(A) R) func(A) R { + return Fn1[A, R](f).Curry() +} + +// Uncurry1 is a function that uncurry one argument function. +func Uncurry1[A, R any](f func(A) R) func(A) R { + return func(a A) R { return f(a) } +} diff --git a/fp/fn2.go b/fp/fn2.go new file mode 100644 index 0000000..21849d9 --- /dev/null +++ b/fp/fn2.go @@ -0,0 +1,31 @@ +package fp + +// Function2 is a function that takes two arguments. +type Function2[A, B, R any] interface { + Apply(A, B) R + Curry() func(A) func(B) R +} + +// Fn2 is a function that takes two arguments. +type Fn2[A, B, R any] func(A, B) R + +func (f Fn2[A, B, R]) Apply(a A, b B) R { return f(a, b) } + +func (f Fn2[A, B, R]) Curry() func(A) func(B) R { + return func(a A) func(B) R { return func(b B) R { return f.Apply(a, b) } } +} + +// Apply2 is a function that takes two arguments. +func Apply2[A, B, R any](f func(A, B) R, a A, b B) R { + return Fn2[A, B, R](f).Apply(a, b) +} + +// Curry2 is a function that curry two arguments function. +func Curry2[A, B, R any](f func(A, B) R) func(A) func(B) R { + return Fn2[A, B, R](f).Curry() +} + +// Uncurry2 is a function that uncurry two arguments function. +func Uncurry2[A, B, R any](f func(A) func(B) R) func(A, B) R { + return func(a A, b B) R { return f(a)(b) } +} diff --git a/fp/fn3.go b/fp/fn3.go new file mode 100644 index 0000000..e6536c2 --- /dev/null +++ b/fp/fn3.go @@ -0,0 +1,33 @@ +package fp + +// Function3 is a function that takes three arguments. +type Function3[A, B, C, R any] interface { + Apply(A, B, C) R + Curry() func(A) func(B) func(C) R +} + +// Fn3 is a function that takes three arguments. +type Fn3[A, B, C, R any] func(A, B, C) R + +func (f Fn3[A, B, C, R]) Apply(a A, b B, c C) R { return f(a, b, c) } + +func (f Fn3[A, B, C, R]) Curry() func(A) func(B) func(C) R { + return func(a A) func(B) func(C) R { + return func(b B) func(C) R { return func(c C) R { return f.Apply(a, b, c) } } + } +} + +// Apply3 is a function that takes three arguments. +func Apply3[A, B, C, R any](f func(A, B, C) R, a A, b B, c C) R { + return Fn3[A, B, C, R](f).Apply(a, b, c) +} + +// Curry3 is a function that curry three arguments function. +func Curry3[A, B, C, R any](f func(A, B, C) R) func(A) func(B) func(C) R { + return Fn3[A, B, C, R](f).Curry() +} + +// Uncurry3 is a function that uncurry three arguments function. +func Uncurry3[A, B, C, R any](f func(A) func(B) func(C) R) func(A, B, C) R { + return func(a A, b B, c C) R { return f(a)(b)(c) } +} diff --git a/fp/fn4.go b/fp/fn4.go new file mode 100644 index 0000000..38493ec --- /dev/null +++ b/fp/fn4.go @@ -0,0 +1,35 @@ +package fp + +// Function4 is a function that takes four arguments. +type Function4[A, B, C, D, R any] interface { + Apply(A, B, C, D) R + Curry() func(A) func(B) func(C) func(D) R +} + +// Fn4 is a function that takes four arguments. +type Fn4[A, B, C, D, R any] func(A, B, C, D) R + +func (f Fn4[A, B, C, D, R]) Apply(a A, b B, c C, d D) R { return f(a, b, c, d) } + +func (f Fn4[A, B, C, D, R]) Curry() func(A) func(B) func(C) func(D) R { + return func(a A) func(B) func(C) func(D) R { + return func(b B) func(C) func(D) R { + return func(c C) func(D) R { return func(d D) R { return f.Apply(a, b, c, d) } } + } + } +} + +// Apply4 is a function that takes four arguments. +func Apply4[A, B, C, D, R any](f func(A, B, C, D) R, a A, b B, c C, d D) R { + return Fn4[A, B, C, D, R](f).Apply(a, b, c, d) +} + +// Curry4 is a function that curry four argument function. +func Curry4[A, B, C, D, R any](f func(A, B, C, D) R) func(A) func(B) func(C) func(D) R { + return Fn4[A, B, C, D, R](f).Curry() +} + +// Uncurry4 is a function that uncurry four argument function. +func Uncurry4[A, B, C, D, R any](f func(A) func(B) func(C) func(D) R) func(A, B, C, D) R { + return func(a A, b B, c C, d D) R { return f(a)(b)(c)(d) } +} diff --git a/fp/fn5.go b/fp/fn5.go new file mode 100644 index 0000000..18e6098 --- /dev/null +++ b/fp/fn5.go @@ -0,0 +1,37 @@ +package fp + +// Function5 is a function that takes five arguments. +type Function5[A, B, C, D, E, R any] interface { + Apply(A, B, C, D, E) R + Curry() func(A) func(B) func(C) func(D) func(E) R +} + +// Fn5 is a function that takes five arguments. +type Fn5[A, B, C, D, E, R any] func(A, B, C, D, E) R + +func (f Fn5[A, B, C, D, E, R]) Apply(a A, b B, c C, d D, e E) R { return f(a, b, c, d, e) } + +func (f Fn5[A, B, C, D, E, R]) Curry() func(A) func(B) func(C) func(D) func(E) R { + return func(a A) func(B) func(C) func(D) func(E) R { + return func(b B) func(C) func(D) func(E) R { + return func(c C) func(D) func(E) R { + return func(d D) func(E) R { return func(e E) R { return f.Apply(a, b, c, d, e) } } + } + } + } +} + +// Apply5 is a function that takes five arguments. +func Apply5[A, B, C, D, E, R any](f func(A, B, C, D, E) R, a A, b B, c C, d D, e E) R { + return Fn5[A, B, C, D, E, R](f).Apply(a, b, c, d, e) +} + +// Curry5 is a function that curry five argument function. +func Curry5[A, B, C, D, E, R any](f func(A, B, C, D, E) R) func(A) func(B) func(C) func(D) func(E) R { + return Fn5[A, B, C, D, E, R](f).Curry() +} + +// Uncurry5 is a function that uncurry five argument function. +func Uncurry5[A, B, C, D, E, R any](f func(A) func(B) func(C) func(D) func(E) R) func(A, B, C, D, E) R { + return func(a A, b B, c C, d D, e E) R { return f(a)(b)(c)(d)(e) } +} diff --git a/fp/fp_test.go b/fp/fp_test.go new file mode 100644 index 0000000..73b76eb --- /dev/null +++ b/fp/fp_test.go @@ -0,0 +1,167 @@ +package fp + +import ( + "strconv" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestCurry(t *testing.T) { + a := qt.New(t) + a.Run("curry1", func(c *qt.C) { + f := func(a int) int { return a + 2 } + curryAdd1 := Curry1[int, int](f) + c.Assert(curryAdd1(1), qt.Equals, 3) + }) + a.Run("curry2", func(t *qt.C) { + add2 := func(a, b int) int { return a + b } + curryAdd2 := Curry2(add2) + t.Assert(curryAdd2(1)(2), qt.Equals, 3) + }) + a.Run("curry3", func(c *qt.C) { + add3 := func(a, b, c int) int { return a + b + c } + curryAdd3 := Curry3(add3) + c.Assert(curryAdd3(1)(2)(3), qt.Equals, 6) + }) + a.Run("curry4", func(c *qt.C) { + add4 := func(a, b, c, d int) int { return a + b + c + d } + curryAdd4 := Curry4(add4) + c.Assert(curryAdd4(1)(2)(3)(4), qt.Equals, 10) + }) + a.Run("curry5", func(c *qt.C) { + add5 := func(a, b, c, d, e int) int { return a + b + c + d + e } + curryAdd5 := Curry5(add5) + c.Assert(curryAdd5(1)(2)(3)(4)(5), qt.Equals, 15) + }) +} + +func TestUncurry(t *testing.T) { + a := qt.New(t) + a.Run("uncurry1", func(c *qt.C) { + f := func(a int) int { return a + 2 } + c.Assert(Uncurry1(f)(1), qt.Equals, 3) + }) + a.Run("uncurry2", func(c *qt.C) { + f := func(a int) func(int) int { + return func(b int) int { return a + b } + } + c.Assert(Uncurry2(f)(1, 2), qt.Equals, 3) + }) + a.Run("uncurry3", func(c *qt.C) { + f := func(a int) func(int) func(int) int { + return func(b int) func(int) int { + return func(c int) int { return a + b + c } + } + } + c.Assert(Uncurry3(f)(1, 2, 3), qt.Equals, 6) + }) + a.Run("uncurry4", func(c *qt.C) { + f := func(a int) func(int) func(int) func(int) int { + return func(b int) func(int) func(int) int { + return func(c int) func(int) int { + return func(d int) int { return a + b + c + d } + } + } + } + c.Assert(Uncurry4(f)(1, 2, 3, 4), qt.Equals, 10) + }) + a.Run("uncurry5", func(c *qt.C) { + f := func(a int) func(int) func(int) func(int) func(int) int { + return func(b int) func(int) func(int) func(int) int { + return func(c int) func(int) func(int) int { + return func(d int) func(int) int { + return func(e int) int { return a + b + c + d + e } + } + } + } + } + c.Assert(Uncurry5(f)(1, 2, 3, 4, 5), qt.Equals, 15) + }) +} + +func TestApply(t *testing.T) { + a := qt.New(t) + a.Run("apply1", func(c *qt.C) { + f := func(a int) int { return a + 2 } + c.Assert(Apply1(f, 1), qt.Equals, 3) + }) + a.Run("apply2", func(c *qt.C) { + f := func(a, b int) int { return a + b } + c.Assert(Apply2(f, 1, 2), qt.Equals, 3) + }) + a.Run("apply3", func(c *qt.C) { + f := func(a, b, c int) int { return a + b + c } + c.Assert(Apply3(f, 1, 2, 3), qt.Equals, 6) + }) + a.Run("apply4", func(c *qt.C) { + f := func(a, b, c, d int) int { return a + b + c + d } + c.Assert(Apply4(f, 1, 2, 3, 4), qt.Equals, 10) + }) + a.Run("apply5", func(c *qt.C) { + f := func(a, b, c, d, e int) int { return a + b + c + d + e } + c.Assert(Apply5(f, 1, 2, 3, 4, 5), qt.Equals, 15) + }) +} + +func TestCompose(t *testing.T) { + a := qt.New(t) + a.Run("compose", func(c *qt.C) { + f := func(a int) int { return a + 1 } + c.Assert(Compose1(f)(1), qt.Equals, 2) + }) + a.Run("compose2", func(c *qt.C) { + f := func(a int) int { return a + 1 } + g := func(a int) int { return a + 2 } + c.Assert(Compose2(f, g)(1), qt.Equals, 4) + }) + + a.Run("compose3", func(c *qt.C) { + f := func(a int) int { return a + 1 } + g := func(a int) int { return a + 2 } + h := func(a int) int { return a + 3 } + c.Assert(Compose3(f, g, h)(1), qt.Equals, 7) + }) + + a.Run("compose4", func(c *qt.C) { + f := func(a int) int { return a + 1 } + g := func(a int) int { return a + 2 } + h := func(a int) int { return a + 3 } + i := func(a int) int { return a + 4 } + c.Assert(Compose4(f, g, h, i)(1), qt.Equals, 11) + }) + + a.Run("compose5", func(c *qt.C) { + f := func(a int) int { return a + 1 } + g := func(a int) int { return a + 2 } + h := func(a int) int { return a + 3 } + i := func(a int) int { return a + 4 } + j := func(a int) int { return a + 5 } + c.Assert(Compose5(f, g, h, i, j)(1), qt.Equals, 16) + }) + + a.Run("multiple type compose", func(c *qt.C) { + f := func(a int) int32 { return int32(a) + 2 } + g := func(a int32) int64 { return int64(a) * 3 } + h := func(a int64) int { return int(a) + 4 } + i := func(a int) int { return a * 100 } + j := func(a int) string { return strconv.FormatInt(int64(a), 10) } + c.Assert(Compose5(f, g, h, i, j)(1), qt.Equals, "1300") + }) +} + +func TestState(t *testing.T) { + a := qt.New(t) + a.Run("state", func(c *qt.C) { + getter, setter := UseState(1) + c.Assert(getter(), qt.Equals, 1) + setter(2) + c.Assert(getter(), qt.Equals, 2) + }) + a.Run("funcState", func(c *qt.C) { + getter, setter := UseFuncState(1) + c.Assert(getter(), qt.Equals, 1) + setter(func(prevState int) int { return prevState + 1 }) + c.Assert(getter(), qt.Equals, 2) + }) +} diff --git a/fp/state.go b/fp/state.go new file mode 100644 index 0000000..d83caa5 --- /dev/null +++ b/fp/state.go @@ -0,0 +1,17 @@ +package fp + +// UseState is a convenience function for creating a stateful function. +func UseState[T any](initial T) (func() T, func(T)) { + state := initial + getter := func() T { return state } + setter := func(newState T) { state = newState } + return getter, setter +} + +// UseFuncState is a convenience function for creating a stateful function. +func UseFuncState[T any](initial T) (func() T, func(func(T) T)) { + state := initial + getter := func() T { return state } + setter := func(f func(prevState T) T) { state = f(state) } + return getter, setter +}