-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from go-board/feat/fp
[fp] add fp functions & curry & compose & state
- Loading branch information
Showing
8 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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))))) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |