fuego

package module
v6.0.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2019 License: Apache-2.0 Imports: 1 Imported by: 0

README

fuego - Functional Experiment in Go.

godoc goreportcard cover.run

Important Note

I am currently working on removing the embedded collections to enable me to focus on core functional aspects. See pull request #5.

I may not re-instate them or perhaps I will create a separate project in the future?

The presence of collections creates a coupling which is detrimental to my ability to evolve this project at a faster pace.

Overview

Making Go come to functional programming.

This is a research project in functional programming which I hope will prove useful!

Fuego brings a few functional paradigms to Go. The intent is to save development time while promoting code readability and reduce the risk of complex bugs.

Install

go get github.com/seborama/fuego

Or for a specific version:

go get gopkg.in/seborama/govcr.v5

Contribute

Contributions and feedback are welcome.

For contributions, you must develop in TDD fashion and ideally provide Go testable examples (if meaningful).

Main features

The code documentation can be found on godoc.

The tests form the best source of documentation. Fuego comes with a good collection of unit tests and testable Go examples. Don't be shy, open them up and read them and tinker with them!

Have fun!!

Entry

Entry is inspired by hamt.Entry. This is an elegant solution from Yota Toyama: the type can be anything so long as it respects the simple behaviour of hamt.Entry. This provides an abstraction of types yet with known behaviour:

  • Hash(): identifies an Entry Uniquely.
  • Equal(): defines equality for a type of Entry. Equal() is expected to be based on Hash().

Several Entry implementations are provided:

  • EntryBool
  • EntryMap
  • EntrySlice
EntryMap

TODO: TBC

EntrySlice

TODO: TBC

Maybe

A Maybe represents an optional value.

When the value is nil, Maybe is considered empty unless it was created with MaybeSome() in which case it is considered to hold the nil value.

MaybeNone() always produces an empty optional.

Tuple

fuego provides these Tuple's:

  • Tuple0
  • Tuple1
  • Tuple2

The values of fuego Tuples is of type Entry but can represent any type (see EntryInt and EntryString examples).

Consumer

TODO: TBC

Functions

See example_function_test.go for basic example uses of Function and BiFunction and the other tests / examples for more uses.

Function

A Function is a normal Go function which signature is

func(i Entry) Entry
BiFunction

A BiFunction is a normal Go function which signature is

func(i,j Entry) Entry

BiFunction's are used with Stream.Reduce() for instance, as seen in stream_test.go.

Stream

A Stream is a wrapper over a Go channel.

NOTE At present, the Go channel is bufferred. This poses ordering issues in parallel processing. It is likely that in a future release this will change. One option is a slice of channels. This improves parallelism but still poses issues with some scenarios where the continuity of values matters (e.g. calculating Fibonacci sequences) as with Reduce, ...

Creation

When providing a Go channel to create a Stream, beware that until you close the channel, the Stream's internal Go function that processes the Stream will remain active. This can lead to a stray Go function.

ƒ.NewStreamFromSlice([]int{1, 2, 3})
// or if you already have a channel of Entry:
c := make(chan Entry, 1e3)
defer close(c)
c <- EntryString("one")
// c <- ...
NewStream(c)
Filter
// See helpers_test.go for "newEntryIntEqualsTo()"
s := ƒ.NewStreamFromSlice([]int{1, 2, 3})
s.Filter(FunctionPredicate(entryIntEqualsTo(EntryInt(1))).
    Or(FunctionPredicate(entryIntEqualsTo(EntryInt(3)))))
// returns []EntryInt{1,3}
Reduce / LeftReduce
// See helpers_test.go for "concatenateStringsBiFunc()"
ƒ.NewStreamFromSlice(string{
    "four",
    "twelve",
    "one",
    "six",
    "three",
}).
Reduce(concatenateStringsBiFunc)
// returns EntryString("four-twelve-one-six-three")
ForEach
total := 0

computeSumTotal := func(value interface{}) {
    total += int(value.(EntryInt).Value())
}

ƒ.NewStreamFromSlice([]int{1, 2, 3}).
ForEach(calculateSumTotal)
// total == 6
Intersperse
ƒ.NewStreamFromSlice([]string{
    "three",
    "two",
    "four",
}).
Intersperse(EntryString(" - "))
// "three - two - four"
GroupBy

Please refer to stream_test.go for an example that groups numbers by parity (odd / even).

Predicates

A Predicate is a normal Go function which signature is

func(t interface{}) bool

A Predicate has convenient pre-defined methods:

  • Or
  • And
  • Not

Several pre-defined Predicate's exist too:

  • True
  • False
  • FunctionPredicate - a Predicate that wraps over a Function

See example_predicate_test.go for some examples.

// ƒ is ALT+f on Mac. For other OSes, search the internet,  for instance,  this page: https://en.wikipedia.org/wiki/%C6%91#Appearance_in_computer_fonts
_ = ƒ.Predicate(ƒ.False).And(ƒ.Predicate(ƒ.False).Or(ƒ.True))(1) // returns false

res := ƒ.Predicate(intGreaterThanPredicate(50)).And(ƒ.True).Not()(23) // res = true

func intGreaterThanPredicate(rhs int) ƒ.Predicate {
    return func(lhs interface{}) bool {
        return lhs.(int) > rhs
    }
}

Known limitations

  • several operations may be memory intensive or poorly performing.

Documentation

Index

Examples

Constants

View Source
const PanicNoSuchElement = "No such element"

PanicNoSuchElement signifies that the iterator does not have the requested element.

Variables

This section is empty.

Functions

func False

func False(t Entry) bool

False is a predicate that returns always false.

func True

func True(t Entry) bool

True is a predicate that returns always true.

Types

type BiFunction

type BiFunction func(e1, e2 Entry) Entry

BiFunction that accepts two arguments and produces a result.

Example

ExampleBiFunction shows how to use BiFunction's. There are more interesting examples through the code. Search for `BiFunction` or the BiFunction signature.

data := []ƒ.Entry{
	EntryString("four"),
	EntryString("twelve"),
	EntryString("one"),
	EntryString("six"),
	EntryString("three")}

res := ƒ.NewStreamFromSlice(data).
	Reduce(concatenateStringsBiFunc)

fmt.Printf("res = %+v\n", res)
Output:

res = four-twelve-one-six-three

type Consumer

type Consumer func(i Entry)

Consumer that accepts one argument and does not return any value.

type Entry

type Entry interface {
	Hash() uint32 // TODO: remove Hash() since the project no longer includes collections? Hashes suffer from collision.
	Equal(Entry) bool
}

Entry is the simplest behaviour that functional types must adhere to.

type EntryBool

type EntryBool bool

EntryBool is an Entry for 'bool'.

func (EntryBool) Equal

func (eb EntryBool) Equal(e Entry) bool

Equal returns true if this type is equal to 'e'.

func (EntryBool) Hash

func (eb EntryBool) Hash() uint32

Hash returns a hash for this Entry.

type EntryMap

type EntryMap map[Entry]EntrySlice

EntryMap is an Entry for 'map[Entry]EntrySlice'.

func (EntryMap) Equal

func (em EntryMap) Equal(e Entry) bool

Equal returns true if this type is equal to 'e'.

func (EntryMap) Hash

func (em EntryMap) Hash() uint32

Hash returns a hash for this Entry.

func (EntryMap) Stream

func (em EntryMap) Stream() Stream

Stream returns a stream of tuples the elements of the EntryMap.

type EntrySlice

type EntrySlice []Entry

EntrySlice is an Entry for '[]Entry'.

func (EntrySlice) Equal

func (es EntrySlice) Equal(e Entry) bool

Equal returns true if this type is equal to 'e'.

func (EntrySlice) Hash

func (es EntrySlice) Hash() uint32

Hash returns a hash for this Entry.

type Function

type Function func(e Entry) Entry

Function that accepts one argument and produces a result.

Example

ExampleFunction shows how to use Function's. There are more interesting examples through the code. Search for `Function` or the Function signature.

timesTwoFunction := timesTwo()
res := timesTwoFunction(EntryInt(7))
fmt.Printf("res = %+v\n", res)
Output:

res = 14

type Functor

type Functor struct {
}

A Functor is a functor TODO: does it make sense to implement this in Golang?

type Maybe

type Maybe struct {
	// contains filtered or unexported fields
}

A Maybe is a maybe monad.

func MaybeNone

func MaybeNone() Maybe

MaybeNone is a Maybe that does not have a value.

func MaybeOf

func MaybeOf(i Entry) Maybe

MaybeOf creates a new Maybe with the given value. If the value is nil then return None otherwise Some(value). Note: MaybeOf(nil) == None() whereas MaybeSome(nil) == MaybeSome(nil).

func MaybeSome

func MaybeSome(i Entry) Maybe

MaybeSome creates a new Maybe with the given value. Note: MaybeOf(nil) == None() whereas MaybeSome(nil) == MaybeSome(nil).

func (Maybe) Get

func (m Maybe) Get() Entry

Get the value of this Maybe or panic if none exists.

func (Maybe) GetOrElse

func (m Maybe) GetOrElse(e Entry) Entry

GetOrElse gets the value of this Maybe or the given Entry if none exists.

func (Maybe) IsEmpty

func (m Maybe) IsEmpty() bool

IsEmpty returns true when this Maybe does not have a value.

func (Maybe) OrElse

func (m Maybe) OrElse(other Maybe) Maybe

OrElse returns this Maybe or the given Maybe if this Maybe is empty.

type Predicate

type Predicate func(t Entry) bool // TODO return EntryBool instead of bool??

Predicate represents a predicate (boolean-valued function) of one argument.

Example

ExamplePredicate shows how to use and combine Predicates.

res := ƒ.Predicate(ƒ.False).Not()(EntryInt(1))
fmt.Printf("Not False == %+v\n", res)

res = ƒ.Predicate(ƒ.True).And(ƒ.False)(EntryInt(1))
fmt.Printf("True and False == %+v\n", res)

res = ƒ.Predicate(ƒ.True).Or(ƒ.False)(EntryInt(1))
fmt.Printf("True or False == %+v\n", res)

// You can use associativity too - part 1 of 2:
// False And False Or True == true
res = ƒ.Predicate(ƒ.False).And(ƒ.False).Or(ƒ.True)(EntryInt(1))
fmt.Printf("False And False Or True == %+v\n", res)

// You can use associativity too - part 2 of 2:
// False And (False Or True) == false
res = ƒ.Predicate(ƒ.False).And(ƒ.Predicate(ƒ.False).Or(ƒ.True))(EntryInt(1))
fmt.Printf("False And (False Or True) == %+v\n", res)
Output:

Not False == true
True and False == false
True or False == true
False And False Or True == true
False And (False Or True) == false
Example (FunctionPredicate)

ExamplePredicate_custom1 shows how to create a custom Predicate using the utility function fuego.FunctionPredicate().

isEvenNumberPredicate := ƒ.FunctionPredicate(isEvenNumberFunction)

res := isEvenNumberPredicate.And(ƒ.True)(EntryInt(23))
fmt.Printf("res = %v", res)
Output:

res = false
Example (Predicate)

ExamplePredicate_custom2 shows how to create a custom Predicate from scratch. Notice how we get all Predicate helpers (And, Or, Not, etc) for "free".

res := intGreaterThanPredicate(50).And(ƒ.True).Not()(EntryInt(23))
fmt.Printf("res = %v", res)
Output:

res = true

func FunctionPredicate

func FunctionPredicate(f Function) Predicate

FunctionPredicate creates a Predicate from a Function.

func (Predicate) And

func (p Predicate) And(other Predicate) Predicate

And is a composed predicate that represents a short-circuiting logical AND of this predicate and another.

func (Predicate) Not

func (p Predicate) Not() Predicate

Not is the logical negation of a predicate.

func (Predicate) Or

func (p Predicate) Or(other Predicate) Predicate

Or is a composed predicate that represents a short-circuiting logical OR of two predicates.

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

Stream is a sequence of elements supporting sequential and parallel parallel operations.

func NewStream

func NewStream(c chan Entry) Stream

NewStream creates a new Stream.

func NewStreamFromSlice

func NewStreamFromSlice(s []Entry) Stream

NewStreamFromSlice creates a new Stream from a Go slice.

func (Stream) Filter

func (s Stream) Filter(predicate Predicate) Stream

Filter returns a stream consisting of the elements of this stream that match the given predicate.

func (Stream) ForEach

func (s Stream) ForEach(consumer Consumer)

ForEach executes the given function for each entry in this stream.

func (Stream) GroupBy

func (s Stream) GroupBy(classifier Function) EntryMap

GroupBy groups the elements of this Stream by classifying them.

Example

ExampleStream_GroupBy shows a use of Stream's with GroupBy.

data := []ƒ.Entry{
	ƒ.Tuple2{E1: EntryInt(1), E2: EntryString("one")},
	ƒ.Tuple2{E1: EntryInt(2), E2: EntryString("two")},
	ƒ.Tuple2{E1: EntryInt(3), E2: EntryString("three")},
	ƒ.Tuple2{E1: EntryInt(4), E2: EntryString("four")},
	ƒ.Tuple2{E1: EntryInt(5), E2: EntryString("five")},
	ƒ.Tuple2{E1: EntryInt(6), E2: EntryString("six")},
	ƒ.Tuple2{E1: EntryInt(7), E2: EntryString("seven")},
	ƒ.Tuple2{E1: EntryInt(8), E2: EntryString("eight")},
	ƒ.Tuple2{E1: EntryInt(9), E2: EntryString("nine")}}

resMap := map[ƒ.Entry]interface{}{}
ƒ.NewStreamFromSlice(data).
	GroupBy(func(i ƒ.Entry) ƒ.Entry {
		return i.(ƒ.Tuple2).E1.(EntryInt) & 1
	}).
	Stream().
	ForEach(func(e ƒ.Entry) {
		resMap[e.(ƒ.Tuple2).E1] = e.(ƒ.Tuple2).E2
	})

for i := 0; i < len(resMap); i++ {
	fmt.Printf("%d => %v\n", i, resMap[EntryInt(i)])
}
Output:

0 => [{2 two} {4 four} {6 six} {8 eight}]
1 => [{1 one} {3 three} {5 five} {7 seven} {9 nine}]

func (Stream) Intersperse

func (s Stream) Intersperse(e Entry) Stream

Intersperse inserts an element between all elements of this Stream.

func (Stream) LeftReduce

func (s Stream) LeftReduce(f2 BiFunction) Entry

LeftReduce accumulates the elements of this Set by applying the given function.

func (Stream) Map

func (s Stream) Map(mapper Function) Stream

Map returns a slice of channel of Set consisting of the results of applying the given function to the elements of this Set

func (Stream) Reduce

func (s Stream) Reduce(f2 BiFunction) Entry

Reduce is an alias for LeftReduce.

type Tuple

type Tuple interface {
	Hash() uint32
	Equal(o Entry) bool
}

A Tuple is a container of value(s). A special case is Tuple0 which does not hold any value.

type Tuple0

type Tuple0 struct{}

Tuple0 is a tuple with 0 element.

func (Tuple0) Arity

func (t Tuple0) Arity() int

Arity is the number of elements in this tuple.

func (Tuple0) Equal

func (t Tuple0) Equal(o Entry) bool

Equal returns true if 'o' and 't' are equal.

func (Tuple0) Hash

func (t Tuple0) Hash() uint32

Hash returns the hash of this tuple.

func (Tuple0) ToSlice

func (t Tuple0) ToSlice() []Entry

ToSlice returns the elements of this tuple as a Go slice.

type Tuple1

type Tuple1 struct {
	E1 Entry
}

Tuple1 is a tuple with 1 element.

func (Tuple1) Arity

func (t Tuple1) Arity() int

Arity is the number of elements in this tuple.

func (Tuple1) Equal

func (t Tuple1) Equal(o Entry) bool

Equal returns true if 'o' and 't' are equal.

func (Tuple1) Hash

func (t Tuple1) Hash() uint32

Hash returns the hash of this tuple.

func (Tuple1) ToSlice

func (t Tuple1) ToSlice() []Entry

ToSlice returns the elements of this tuple as a Go slice.

type Tuple2

type Tuple2 struct {
	E1 Entry
	E2 Entry
}

Tuple2 is a tuple with 2 elements.

func (Tuple2) Arity

func (t Tuple2) Arity() int

Arity is the number of elements in this tuple.

func (Tuple2) Equal

func (t Tuple2) Equal(o Entry) bool

Equal returns true if 'o' and 't' are equal.

func (Tuple2) Hash

func (t Tuple2) Hash() uint32

Hash returns the hash of this tuple.

func (Tuple2) ToSlice

func (t Tuple2) ToSlice() []Entry

ToSlice returns the elements of this tuple as a Go slice.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL