partial

package
v2.12.0 Latest Latest
Warning

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

Go to latest
Published: May 13, 2024 License: MIT Imports: 0 Imported by: 0

Documentation

Overview

Package partial provides helpers for partial function application.

Each function in this package has a suffix for arity that indicates the number of arguments of the provided input function f.

Each Left/Right function returns a function with a single argument that can be used to "partially apply" input function f with one argument already bound.

Functions have the prefix "Left" if they bind the left-most argument first, or "Right" if they bind the right-most argument first. The prefix "All" binds all arguments at once.

Each input function must return exactly one value. The "fun/result" and "fun/maybe" packages provide useful functions that can wrap functions that return (value, ok) or (value, error) result types into single-value return variants.

For example,

f(a, b, c) => x becomes g(b, c) => x, with a bound, by calling Left3(f)(a).

f(a, b, c) => x becomes g(a, b) => x, with c bound, by calling Right3(f)(c).

f(a, b, c) => x becomes g() => x, with a, b, and c bound, by calling All3(f)(a, b, c).

f(a) => x becomes g() => x, with a bound, by calling Single(f)(a).

Example (All)
package main

import (
	"fmt"
	"math"

	"github.com/tawesoft/golib/v2/fun/partial"
)

func main() {
	// Pythagoras theorem for calculating the hypotenuse of a triangle:
	// a squared + b squared = c squared.
	hyp := func(a float64, b float64) float64 {
		return math.Sqrt((a * a) + (b * b))
	}

	// Suppose we want a function with no arguments that returns the answer
	// for a specific triangle. Imagine it's an expensive computation, we've
	// got lots of computations like this, and they're ones we might want to
	// run asynchronously across multiple workers. This is a simple "promise"
	// construct.
	//
	// Normally we could define it like this:
	hyp_2_3_verbose := func() float64 { return hyp(2, 3) }

	// But we can use "partial.All*" functions to do this for us (here, 2,
	// for the two arguments).
	hyp_2_3_terse := partial.All2(hyp)(2, 3)

	fmt.Printf("hyp_2_3_verbose = %.3f\n", hyp_2_3_verbose())
	fmt.Printf("hyp_2_3_terse = %.3f\n", hyp_2_3_terse())

}
Output:

hyp_2_3_verbose = 3.606
hyp_2_3_terse = 3.606
Example (Line)
package main

import (
	"fmt"

	"github.com/tawesoft/golib/v2/fun/partial"
)

func main() {
	// The formula for a line can be given by "y = mx + c", where m is the
	// gradient, and c is the offset where the line crosses the x-axis.
	line := func(x int, m int, c int) int { // solves for y
		return (m * x) + c // y = mx + c
	}

	// That's the general formula. Let's say we know c and m, and instead want
	// a single function y = f(x) that we can plug in values for x and get
	// a result for y.

	// For example, y = 3x + 5 where f(n) is f((3*n) + 5).

	// Given c, we can partially apply the function so y = f(mx) where
	// f(n) = n+5. To put it another way, c is now bound, and we only need m
	// and x as inputs now.
	lineC := partial.Right3(line)(5) // f(x, m, c) => f(x, m)

	// Given m, we can partially apply the function so y = f(g(x)) where g(n)
	// = m * n. To put it another way, m is now also bound, and we only need x
	// as an input now.
	lineMC := partial.Right2(lineC)(2) // f(x, m) => f(x)

	// Now we have a function y = 2x + 5.

	// Inputting x = 3...
	fmt.Println(lineMC(3)) // y = (2 * 3) + 5 = 11

}
Output:

11
Example (Maybe)
package main

import (
	"fmt"

	"github.com/tawesoft/golib/v2/fun/maybe"
	"github.com/tawesoft/golib/v2/fun/partial"
)

func main() {
	// divides two numbers, while checking for divide by zero.
	divide := func(x int, y int) (value int, ok bool) {
		if y == 0 {
			return 0, false
		}
		return x / y, true
	}

	// we need a single return value, so convert the function to one that
	// returns a single [maybe.M].
	maybeDivide := maybe.WrapFunc2(divide)

	// we create a function, divider, with one argument, that can be used to
	// construct new functions that divide by a constant factor.
	divider := partial.Right2(maybeDivide)

	// bind a constant factor to the divide function, and also convert it back
	// to a function that returns (value int, ok bool) instead of a [maybe.M].
	divideByTwo := maybe.UnwrapFunc(divider(2))
	divideByZero := maybe.UnwrapFunc(divider(0))

	{
		result, ok := divideByTwo(10)
		fmt.Printf("divideByTwo(10) = %d, %t\n", result, ok)
	}
	{
		result, ok := divideByZero(10)
		fmt.Printf("divideByZero(10) = %d, %t\n", result, ok)
	}

}
Output:

divideByTwo(10) = 5, true
divideByZero(10) = 0, false

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func All2 added in v2.10.0

func All2[A any, B any, Return any](
	f func(A, B) Return,
) func(A, B) func() Return

func All3 added in v2.10.0

func All3[A any, B any, C any, Return any](
	f func(A, B, C) Return,
) func(A, B, C) func() Return

func All4 added in v2.10.0

func All4[A any, B any, C any, D any, Return any](
	f func(A, B, C, D) Return,
) func(A, B, C, D) func() Return

func Left2

func Left2[A any, B any, Return any](
	f func(a A, b B) Return,
) func(a A) func(b B) Return

func Left3

func Left3[A any, B any, C any, Return any](
	f func(a A, b B, c C) Return,
) func(a A) func(b B, c C) Return

func Left4

func Left4[A any, B any, C any, D any, Return any](
	f func(a A, b B, c C, d D) Return,
) func(a A) func(b B, c C, d D) Return

func Right2

func Right2[A any, B any, Return any](
	f func(a A, b B) Return,
) func(b B) func(a A) Return

func Right3

func Right3[A any, B any, C any, Return any](
	f func(a A, b B, c C) Return,
) func(c C) func(a A, b B) Return

func Right4

func Right4[A any, B any, C any, D any, Return any](
	f func(a A, b B, c C, d D) Return,
) func(d D) func(a A, b B, c C) Return

func Single added in v2.2.0

func Single[T any, Return any](
	f func(t T) Return,
) func(t T) func() Return

Single takes a function with a single argument and return value and constructs a function that takes a single argument and returns a function that takes no arguments and returns a single value.

For example,

opener :=  partial.Single(result.WrapFunc(os.Open))
fooOpener := opener("foo.txt")
f, err := fooOpener().Unpack()

Types

This section is empty.

Jump to

Keyboard shortcuts

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