Skip to content

Repository files navigation

Build Status

Fungebra

Wrapper to allow algebraic manipulation and composition of functions. An abstract callable provides methods for chaining and combining functions, and defines an expression syntax for doing the same.

The aims of this package are purely academic; I do not recommend using this if you respect the people who must read your code.

fromfungebraimportFunctionfromexternal_libraryimportother_func@Functiondefmy_func():
...
other_func=Function(other_func)

Expression syntax

In the following examples:

  • f, g, h are Function wrapped callables.
  • x is an arbitrary argument.

Function composition

(f+g)(x) ==f.compose(g)(x) ==f(g(x))
(f|g)(x) ==f.pipe(g)(x) ==g(f(x))
[1, 2, 3] |f==f([1, 2, 3])
[1, 2, 3] |f.collect==f(1, 2, 3)

Partial application

(f<< (1, 2, 3))(x) ==f.partial(1, 2, 3)(x) ==f(1, 2, 3, x)
(f<< {"opt": "val"})(x) ==f.partial({"opt": "val"})(x) ==f(x, opt="val")
(f<<Args(1, 2, opt="val"))(x) ==f.partial(1, 2, opt="val")(x) ==f(1, 2, x, opt="val")
(f>> (1, 2, 3))(x) ==f.rpartial(1, 2, 3)(x) ==f(x, 1, 2, 3)

Map, filter, reduce

Map

(-f)(x) ==f.map(x) ==map(f, x)
(f-g)(x) ==f.compose(g.map)(x) ==map(g, f(x))

Filter

(f<g)(x) ==f.filter(g)() ==filter(g, f(x))

Reduce

(f>g)(x) ==f.reduce(g)(x) ==reduce(g, f(x))

Combining

(-f>g)(x) == (f>=g)(x) ==f.map.reduce(g)(x) ==reduce(g, map(f, x))
(-f<g>h)(x) ==f.map.filter(g).reduce(h)(x) ==reduce(h, filter(g, map(f, x)))
(f<=g)(x) ==f.map.filter(g)(x) ==filter(g, map(f, x))

Functions

A number of compatible Function callables are provided in fungebra.functions. The operator standard library is re-exported as Function objects.

Examples

fromfungebraimportFunction, operatorasopfromfungebra.functionsimportless, fnot, itemgettergreater_or_equal=less|fnot@Functiondefget_old_items_sort_by_name_desc(min_age: int):
"""Filter a known schema on old items, and sort by name descending."""returnitemgetter("hits").filter(
itemgetter("age") |greater_or_equal(min_age)
).pipe(
F(sorted) << {"key": itemgetter("name") |ord|op.neg}
).pipe(list)
get_old_items_sort_by_name_desc(2)(
{
"hits": [
{"age": 1, "name": "B"},
{"age": 2, "name": "A"},
{"age": 3, "name": "C"},
]
}
) == [
{"age": 3, "name": "C"},
{"age": 2, "name": "A"},
]
fromfungebraimportFunctionfromfungebra.functionsimport (
iffy, less, constantly, caller
)
@Functiondeftruncate_below(minimum: int) ->int: """Truncate arguments below a threshold."""returncaller(minimum).map([less, constantly]) |iffy.expandtruncate_below(0).lmap([-1, 2, 4]) == [0, 2, 4]

Requirements

This package is currently tested for Python 3.6.

Installation

This project is not currently packaged and so must be installed manually.

Clone the project with the following command:

git clone https://github.com/jacksmith15/fungebra.git

Package requirements may be installed via pip install -r requirements.txt. Use of a virtualenv is recommended.

Development

  1. Clone the repository: git clone git@github.com:jacksmith15/fungebra.git && cd fungebra
  2. Install the requirements: pip install -r requirements.txt -r requirements-test.txt
  3. Run pre-commit install
  4. Run the tests: bash run_test.sh -c -a

This project uses the following QA tools:

  • PyTest - for running unit tests.
  • PyLint - for enforcing code style.
  • MyPy - for static type checking.
  • Travis CI - for continuous integration.
  • Black - for uniform code formatting.

License

This project is distributed under the MIT license.

About

Algebraic manipulation and composition of Python functions (for fun)

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages