Skip to content

Commit

Permalink
feat(types): Added SupplyFunc and CallbackFunc
Browse files Browse the repository at this point in the history
  • Loading branch information
protoman92 committed Mar 27, 2018
1 parent a533090 commit 9a89262
Show file tree
Hide file tree
Showing 17 changed files with 284 additions and 101 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

[![Go Report Card](https://goreportcard.com/badge/github.com/protoman92/gocompose)](https://goreportcard.com/report/github.com/protoman92/gocompose)
[![Build Status](https://travis-ci.org/protoman92/gocompose.svg?branch=master)](https://travis-ci.org/protoman92/gocompose)
[![Coverage Status](https://coveralls.io/repos/github/protoman92/gocompose/badge.svg?branch=master)](https://coveralls.io/github/protoman92/gocompose?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/protoman92/gocompose/badge.svg?branch=master&dummy=true)](https://coveralls.io/github/protoman92/gocompose?branch=master)

Composable functions for Go. This is an experimental library that I use mainly for personal projects.
38 changes: 38 additions & 0 deletions compose/callbackFunc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package compose

// CallbackFunc represents an function that accepts a value and perform side.
// effects. This is a specialized form of Func, and thus is constructible from
// a Func.
type CallbackFunc func(interface{}) error

// CallbackFuncConvertible represents an object that can be converted to a
// CallbackFunc.
type CallbackFuncConvertible interface {
ToCallbackFunc() CallbackFunc
}

// ToCallbackFunc returns the current CallbackFunc.
func (cf CallbackFunc) ToCallbackFunc() CallbackFunc {
return cf
}

// Invoke invokes the underlying function.
func (cf CallbackFunc) Invoke(value interface{}) error {
return cf(value)
}

// ToFunc converts the current CallbackFunc into a ToFunc.
func (cf CallbackFunc) ToFunc() Func {
return func(value interface{}) (interface{}, error) {
err := cf(value)
return nil, err
}
}

// ToCallbackFunc converts the current Func into an ToCallbackFunc.
func (f Func) ToCallbackFunc() CallbackFunc {
return func(value interface{}) error {
_, err := f.Invoke(value)
return err
}
}
45 changes: 45 additions & 0 deletions compose/callbackFuncF.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package compose

// CallbackFuncF converts an CallbackFunc to another CallbackFunc.
type CallbackFuncF func(CallbackFunc) CallbackFunc

// CallbackFuncFConvertible represents an object that can be converted to a
// CallbackFuncF.
type CallbackFuncFConvertible interface {
ToCallbackFuncF() CallbackFuncF
}

// ToCallbackFuncF returns the current CallbackFuncF.
func (cff CallbackFuncF) ToCallbackFuncF() CallbackFuncF {
return cff
}

// Wrap is a convenience method that calls the underlying CallbackFuncF.
func (cff CallbackFuncF) Wrap(ef CallbackFunc) CallbackFunc {
return cff(ef)
}

// ToFuncF converts the current CallbackF into a ToFuncF.
func (cff CallbackFuncF) ToFuncF() FuncF {
return func(f Func) Func {
callback := func(value interface{}) error {
_, err := f.Invoke(value)
return err
}

return cff.Wrap(callback).ToFunc()
}
}

// ToCallbackFuncF converts a FuncF to an ToCallbackFuncF. This method should be
// used at the last stage of a chain so we do not need to redefine other higher
// order functions.
func (ff FuncF) ToCallbackFuncF() CallbackFuncF {
return func(cf CallbackFunc) CallbackFunc {
var function Func = func(value interface{}) (interface{}, error) {
return nil, cf(value)
}

return ff.Wrap(function).ToCallbackFunc()
}
}
52 changes: 52 additions & 0 deletions compose/conversion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package compose

import (
"testing"
)

func TestConvertBetweenFuncTypes(t *testing.T) {
/// Setup
var errF Func = func(value interface{}) (interface{}, error) {
return valueOp, errOp
}

/// When
value, err := errF.
ToCallbackFunc().
ToCallbackFunc().
ToFunc().
ToFunc().
ToSupplyFunc().
ToSupplyFunc().
ToFunc().
Invoke(0)

/// Then
if err != errOp || value != nil {
t.Errorf("Expected %v, got %v", errOp, err)
}
}

func TestCovertBetweenFuncF(t *testing.T) {
/// Setup
var errF Func = func(value interface{}) (interface{}, error) {
return valueOp, errOp
}

/// When
value, err := RetryF(retries).
ToCallbackFuncF().
ToCallbackFuncF().
ToFuncF().
ToFuncF().
ToSupplyFuncF().
ToSupplyFuncF().
ToFuncF().
Wrap(errF).
Invoke(0)

/// Then
if err != errOp || value != nil {
t.Errorf("Expected %v, got %v", errOp, err)
}
}
10 changes: 0 additions & 10 deletions compose/errorFunc.go

This file was deleted.

27 changes: 0 additions & 27 deletions compose/errorFuncF.go

This file was deleted.

24 changes: 13 additions & 11 deletions compose/function.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package compose

// Func represents an operation that could return an error.
type Func func() (interface{}, error)
type Func func(interface{}) (interface{}, error)

// Invoke is a convenience method to call a Func. Although it is the same as if
// we call the function normally, this may look nicer in a chain.
func (f Func) Invoke() (interface{}, error) {
return f()
// FuncConvertible represents an object that can be converted to a Func.
type FuncConvertible interface {
ToFunc() Func
}

// ToFunc returns the current Func.
func (f Func) ToFunc() Func {
return f
}

// ErrorFunc converts the current function into an ErrorFunc.
func (f Func) ErrorFunc() ErrorFunc {
return func() error {
_, err := f.Invoke()
return err
}
// Invoke is a convenience method to call a Func. Although it is the same as if
// we call the function normally, this may look nicer in a chain.
func (f Func) Invoke(value interface{}) (interface{}, error) {
return f(value)
}
20 changes: 12 additions & 8 deletions compose/functionF.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ package compose
// FuncF transforms a Func into another Func.
type FuncF func(Func) Func

// FuncFConvertible represents an object that can be converted to a FuncF.
type FuncFConvertible interface {
ToFuncF() FuncF
}

// ToFuncF returns the current FuncF.
func (ff FuncF) ToFuncF() FuncF {
return ff
}

// Compose composes the functionalities of both FuncF. We can use this to chain
// enhance a base Func without exposing implementation details.
func (ff FuncF) Compose(selector FuncF) FuncF {
func (ff FuncF) Compose(selector FuncFConvertible) FuncF {
return func(f Func) Func {
return ff(selector(f))
return ff(selector.ToFuncF().Wrap(f))
}
}

// ComposeFn is similar to Compose, but it is more convenient when we deal with
// functions that return FuncF.
func (ff FuncF) ComposeFn(selectorFn func() FuncF) FuncF {
return ff.Compose(selectorFn())
}

// Wrap is a convenience method to invoke the wrap on a Func. This may look
// nicer in a function chain.
func (ff FuncF) Wrap(f Func) Func {
Expand Down
19 changes: 10 additions & 9 deletions compose/functionF_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"testing"
)

func TestCompose(t *testing.T) {
func TestComposeFuncF(t *testing.T) {
published := 0

retryF := RetryF(retries)
Expand All @@ -18,37 +18,38 @@ func TestCompose(t *testing.T) {
}

/// When && Then 1
retryF.Compose(publishF).ComposeFn(NoopF)(errF)()
retryF.Compose(publishF).Compose(NoopF()).ToSupplyFuncF().Wrap(errF).Invoke()

if uint(published) != retries+1 {
t.Errorf("Expected %d, got %d", retries+1, published)
}

/// When && Then 2
published = 0
publishF.Compose(retryF).ComposeFn(NoopF)(errF)()

publishF.Compose(retryF).Compose(NoopF()).ToSupplyFuncF().Wrap(errF).Invoke()

if published != 1 {
t.Errorf("Expected %d, got %d", 1, published)
}
}

func TestComposeConvertToErrorFuncF(t *testing.T) {
func TestComposeConvertToCallbackFuncF(t *testing.T) {
/// Setup
errF := func() error {
errF := func(value interface{}) error {
return errOp
}

retryF := RetryF(retries).ErrorFuncF()
retryF := RetryF(retries).ToCallbackFuncF()

/// When & Then
if err := retryF.Wrap(errF).Invoke(); err != errOp {
if err := retryF.Wrap(errF).Invoke(nil); err != errOp {
t.Errorf("Expected %v, got %v", errOp, err)
}
}

func BenchmarkComposition(b *testing.B) {
errF := func() (interface{}, error) {
errF := func(value interface{}) (interface{}, error) {
return valueOp, errOp
}

Expand All @@ -62,7 +63,7 @@ func BenchmarkComposition(b *testing.B) {
Compose(composeF)(errF)

for i := 0; i < b.N; i++ {
composed()
composed(nil)
}
}

Expand Down
6 changes: 3 additions & 3 deletions compose/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package compose

import "testing"

func TestConvertToErrorFunc(t *testing.T) {
func TestConvertToCallbackFunc(t *testing.T) {
/// Setup
var errF Func = func() (interface{}, error) {
var errF Func = func(value interface{}) (interface{}, error) {
return valueOp, errOp
}

/// When & Then
if err := errF.ErrorFunc().Invoke(); err != errOp {
if err := errF.ToCallbackFunc().Invoke(nil); err != errOp {
t.Errorf("Expected %v, got %v", errOp, err)
}
}
4 changes: 2 additions & 2 deletions compose/noop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import "testing"

func TestNoop(t *testing.T) {
/// Setup
var errF Func = func() (interface{}, error) {
var errF Func = func(value interface{}) (interface{}, error) {
return valueOp, errOp
}

/// When & Then
if _, err := errF.Noop().Invoke(); err != errOp {
if _, err := errF.Noop().Invoke(nil); err != errOp {
t.Errorf("Expected %v, got %v", errOp, err)
}
}
4 changes: 2 additions & 2 deletions compose/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package compose
// PublishF publishes the result of a Func for side effects.
func PublishF(callback func(interface{}, error)) FuncF {
return func(f Func) Func {
return func() (interface{}, error) {
value, err := f()
return func(value interface{}) (interface{}, error) {
value, err := f(value)
callback(value, err)
return value, err
}
Expand Down
4 changes: 2 additions & 2 deletions compose/publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func TestPublish(t *testing.T) {
var publishedValue interface{}
var publishedErr error

var errF Func = func() (interface{}, error) {
var errF Func = func(value interface{}) (interface{}, error) {
return valueOp, errOp
}

Expand All @@ -21,7 +21,7 @@ func TestPublish(t *testing.T) {
}

/// When & Then
value, err := errF.Publish(publishF).Retry(retries).Invoke()
value, err := errF.Publish(publishF).Retry(retries).ToSupplyFunc().Invoke()

if err != errOp || value != nil {
t.Errorf("Expected %v, got %v", errOp, err)
Expand Down

0 comments on commit 9a89262

Please sign in to comment.