Skip to content

Latest commit

 

History

History
344 lines (263 loc) · 8.43 KB

README.md

File metadata and controls

344 lines (263 loc) · 8.43 KB

fluent Build status Test coverage

Functional constructs (lent from other languages) for Go lang 1.18

Option

Option represents an optional value.

Option can be used as alternative for:

  • Uninitialized values
  • Invalid/empty return values
  • os.IsNotExist(e error)

This implementation is based on Java's java.util.Optional and Rust's std::option.

Option API

type Option[T any] interface {

        // Returns true if a value is present.
        IsPresent() bool

        // Gets the Option value or panics if empty
        Get() T

        // Applies the provided mapper function over the Option value, if present.
        Map(mapper func(T) T) Option[T]

        // Returns the Option value if present, or the provided value it empty.
        OrElse(other T) T

        // Returns the Option value if present, or invoke the provided function if
        // empty.
        OrElseGet(func() T) T

        // If empty, calls the provided function and returns it result or returns
        // the current option if present.
        Or(func() Option[T]) Option[T]

        // Returns the Option value wrapped into a result if present. If empty,
        // returns a Result with a wrapped error.
        OrError(e error) Result[T]

        // Executes the provided function if a value is present
        IfPresent(func(T))

        // If a value is present and the value is accepted by the provided filter,
        // returns an Option representing the value, otherwise will return an empty
        // Option
        Filter(func(T) bool) Option[T]

        String() string
}  

// Present returns a new Option wrapping the T value.
func Present[T any](value T) Option[T]

// Empty returns an empty Option.
func Empty[T any]() Option[T]

// MapOption executes the mapper function over the Option value if it is not
// empty.
//
// If the Option is empty, the mapper is not executed and an empty Option is
// returned.
func MapOption[T any, R any](o Option[T], mapper func(T) R) Option[R]

// OfNillable returns an Option describing the provided reference.
//
// If the provided reference of T is non-nil, a Option representing the value
// is returned.
//
// If the value is nil, an empty option is returned.
func OfNillable[T any](ref *T) Option[*T]

Option Examples

package main

import (
  "github.com/mikhasd/fluent" 
  "fmt"
)

func Divide(a, b int) fluent.Option[int] {
	if b == 0 {
		return fluent.Empty[int]()
	} else {
		return fluent.Present(a / b)
	}
}

func Double(a int) int {
	return a * 2
}

func String(a int) string {
	return fmt.Sprintf("%d", a)
}

func ExampleOption_goodDivision() {
	// Mapping operations can be chained if input and output types are the same
	var option fluent.Option[int] = Divide(6, 3).Map(Double)
	// But mapping operations with different input and output types must use
	// package level functions due to language limitations.
	message := fluent.MapOption(option, String)

	var result string
	if message.Present() {
		result = message.Get()
	} else {
		result = "empty"
	}

	fmt.Println(result)
	// Output: 4
}

func ExampleOption_badDivision() {
	var option fluent.Option[int] = Divide(100, 0).Map(Double)
	message := fluent.MapOption(option, String)

	var result string
	if message.Present() {
		result = message.Get()
	} else {
		result = "empty"
	}

	fmt.Println(result)
	// Output: empty
}

Result

Result represents the output of a function which may have been computed successfully (Ok) or failed with an error (Err).

Result API

type Result[T any] interface {
	// Returns true if the Result is Ok
	IsOk() bool

	// Returns true if the Result is Err
	IsErr() bool

	// Converts the Result into an Option.
	//
	// If the Result is Ok, an Option will be returned, wrapping the result
	// value.
	//
	// If the Result is Err, an empty Option will be returned.
	Ok() Option[T]

	// Converts the Result into an Option.
	//
	// If the Result is Err, an Option will be returned, wrapping the result
	// error.
	//
	// If the Result is Ok, an empty Option will be returned.
	Err() Option[error]

	// Executes the mapper function over the Result value.
	//
	// If the Result is Err, the function is not executed.
	Map(mapper func(T) T) Result[T]

	// Executes the mapper function over the Result error.
	//
	// If the Result is Ok, the function is not executed.
	MapErr(func(error) T) Result[T]

	// Gets the Result value.
	//
	// This function will panic if the Result is Err.
	Get() T

	// Gets the Result error.
	//
	// This function will panic if the Result is Ok.
	GetErr() error

	// Gets the Result value or	the provided value if if the Result is Err.
	OrElse(value T) T

	// Calls the provided function if the Result is Err. Returns Result value
	// if Result is Ok.
	OrElseGet(func() T) T

	// Calls the provided function if the Result is Err. Returns the current
	// Result if Ok.
	Or(func() Result[T]) Result[T]

	String() string
}

// Ok returns a result representing a successful operation resulting in
// the value T.
func Ok[T any](value T) Result[T]

// Err returns a result representing a failed operation which resulted in
// on an error.
func Err[T any](e error) Result[T]

// MapResult executes the mapper function over the Result value if it Ok.
//
// If the Result is Err, the mapper is not executed and an Err Result is
// returned.
func MapResult[T any, R any](r Result[T], mapper func(T) R) Result[R]

// CallResult executes the provided function and returns an Ok result if
// the result error is nil.
//
// If the provided function returns an error, an Err result is returned, even
// if a result is returned.
func CallResult[T any](fn func() (T, error)) Result[T]

Result Examples

package main

import (
	"github.com/mikhasd/fluent"
	"embed"
	"fmt"
)

//go:embed README.md
var sourceFiles embed.FS

func FileBytes(fileName string) fluent.Result[[]byte] {
	// ResultFromCall returns an error Result if the result err is equal to nil
	// or Ok if the error is not present.
	return fluent.CallResult(func() ([]byte, error) {
		data, err := sourceFiles.ReadFile(fileName)
		return data, err
	})
}

func ExampleResult_goodFile() {
	r := FileBytes("README.md")
	msg := fluent.MapResult(r, func(b []byte) string {
		return "has file"
	})

	var result string
	if msg.IsOk() {
		result = msg.Get()
	} else {
		result = "empty"
	}

	fmt.Println(result)
	// Output: has file
}

func ExampleResult_badFile() {
	r := FileBytes("badfile")
	msg := fluent.MapResult(r, func(b []byte) string {
		return "has file"
	})

	var result string
	if msg.IsOk() {
		result = msg.Get()
	} else {
		result = "empty"
	}

	fmt.Println(result)
	// Output: empty
}

array

The array package contains functions to facilitate working with arrays.

array API

// Map creates a new array populated with the result of calling the provided
// `mapper` function on every element of the input array.
func Map[I any, O any](in []I, mapper func(I) O) []O

// Filter creates a new array with all elements that pass the provided
// `condition` function.
func Filter[T any](in []T, condition func(T) bool) []T

Iterator

An Iterator facilitates traversing a collection of elements of know or unknown size.

Iterator API

type Iterator[T any] interface {
	// Next advances the iteration and return the next value.
	// An empty `fluent.Option` will be returned when the iteration finishes.
	Next() fluent.Option[T]
}

// FromArray creates a new iterator for a given array.
func FromArray[T any](elements []T) Iterator[T]

// Of creates a new `Iterator` for the elements provided as arguments.
func Of[T any](elements ...T) Iterator[T]

// MapKeys creates an `Iterator` with the keys of a given map.
func MapKeys[K comparable, V any](m map[K]V) Iterator[K]

// MapValues creates an `Iterator` with the values of a given map.
func MapValues[K comparable, V any](m map[K]V) Iterator[V]

// FromMap creates a new `Iterator` for the keys and values of a given map.
func FromMap[K comparable, V any](m map[K]V) Iterator[MapEntry[K, V]]

stream