Skip to main content

fp

A lightweight functional programming library with numpy, jax and torch support.

Installation

There are two ways that you can start using fp. This library is still under development, but beta versions will updated as necessary on PyPI.

poetry environment

git clone https://github.com/opeltre/fp
cd fp && poetry install

pip install

pip install funprogram

Overview

The fp library relies on python metaclasses to emulate a static python type system of Type instances.

Its motivation is to provide a strict yet flexible interface to a polymorphic type system, implementing most the abstractions of a cartesian closed category. Being familiar with category theory is not a prerequisite for using fp. The package and documentation are also intended as a user-friendy and didactic tools for getting used with functional programming concepts.

Type polymorphism was a necessary feature for the downstream message-passing library topos that I started developing during my PhD. Overcoming the mysterious difficulties of metaclass construction was a hard enough task for this latter project to ever really reach a definitive state, but development might perhaps resume as stable versions of the fp package are released.

fp.cartesian module

The type Hom(A, B) declares typed functions or type morphisms, with input in A and output in B. Functions with multiple inputs can be declared by supplying a tuple of types A = (A0, A1, ...) as input.

from fp import *

@Hom((Int, Str), Str)
def foo(n, s):
    return ("-" * n).join([s] * n)

In fp, typed functions are instances of the "top" type Hom.Object which takes care automatic currying, i.e. returning the partial application of the decorated callable when invoked with too few arguments.

>>> foo
Int -> Str -> Str: foo
>>> foo(2)
Str -> Str: foo 2
>>> foo(2)("Hello World!")
Str: "Hello World!--Hello World! "

Typed functions (including curried ones) can be composed with the @ operator:

>>> bar = Hom(Int, Str)(lambda n: '|' * n)
>>> baz = Int.mul
>>> foo(4) @ bar @ baz 
Int -> Str: foo 4 . λ . mul 3
>>> (foo(4) @ bar @ baz(3))(2)
Str: "||||||----||||||----||||||----||||||"

fp.instances module

Algebraic subclasses of Type are defined in fp.instances.algebra, allowing transparent subclassing of numeric builtin types. The lifting and propagation of algebraic methods defined there is also used by Str and List.Object, by being declared instances of the Monoid type class.

>>> from fp import Int, Float, Str, List
>>> greet = Str.add("👋 Welcome")
>>> greet 
Str -> Str: add 👋 Welcome
>>> greet("! " + foo(2)(bar(3)))
"👋 Welcome! |||--|||"

The List functor creates types inheriting from List.Objecŧ, a subclass of list with a map method. The map method of any functorial type, e.g. List(Str), is actually an alias for the List.fmap class method. Only the target needs to be explicited when called with an untyped function.

>>> phone = List(Int)("0632501202")
>>> phone.map(lambda n: 2 << n, tgt=Int)
List Int : [1, 64, 8, 4, 32, 1, 2, 4, 1, 4]
>>> List.fmap(bar)
List Int -> List Str: map λ
>>> List.fmap(bar)(phone)
List Str : ['', '||||||', '|||', '||', '|||||', '', '|', '||', '', '||']

Other features include a State monad with which one might indeed write fun programs 💻🐒!

# `State(Int, Str)` subclasses `Int -> (Int, Str)`

@State(Int, Str)
def barbaz(n):
    """Update an integer and return a string."""
    q, r = divmod(n, 2)
    if q == 0:
        return q, "|" if r else "*"
    return q, "|<" if r else "*<"

# The `State(Int)` monad binds `a -> State(Int, b)` functions

@Hom(Str, State(Int, Str))
def foobarbaz(acc):
    """Do `barbaz` while the accumulator says to continue."""
    # run with io.VERBOSITY = 1 to see intermediate steps
    io.log(acc, v=1)
    if len(acc) and acc[-1] == "<":
        # accumulate output
        accbarbaz = barbaz.map(Str.add(acc[:-1]))
        # bind foobarbaz
        return accbarbaz >> foobarbaz
    else: 
        # return accumulator
        return State(Int).unit(acc)

>>> foobarbaz.run(257)
(Int, Str): (0, '|*******|')
>>> foobarbaz(260)
Str: '**|*****|'

Other monads have found plenty of applications in abstracting IO contexts, error handling, data streams and asynchronous threads.

See the docs or the source for an exhaustive list of the currently implemented types, functors, monads, etc.

fp.tensors module

For now, the Tensor class is an alias of Torch, while Arrays from other backends can be explictly created with Jax and Numpy. This part of the API is subject to change.

>>> from fp.tensors import Torch, Jax, Numpy
>>> from fp.tensors import backends
>>> backends
List Backend: [Numpy, Jax, Torch]
>>> x, y, z = (B.range(3) * (10 ** i) for i, B in enumerate(backends))
>>> x
Numpy: [0 1 2]
>>> y + x.jax() 
Jax: [0 11  22]
>>> z + (x + y.numpy()).torch()
Torch: [0, 111, 222]

Typed tensors are created by supplying shape tuples to the Tens functor. With Linear and Otimes, typed tensors form a closed monoidal subcategory of Type.

>>> from fp.tensors import Tens, Linear
>>> E = Tens((2, 3))
>>> F = Tens((4,))
>>> v = E.range()
Tens 2x3 : [[0, 1, 2],
           [[3, 4, 5]]
>>> f = Linear(E, F)(Tensor.randn(4, 6))
>>> f(v).shape
(4,)

See examples/arrays.py and the docs for more details.

Contributing and troubleshooting

If you use fp and experience bugs or inconsistencies, please report an issue on the github page. When debugging fp code, setting the following should be useful:

fp.io.VERBOSITY = 3

Release files for funprogram 0.9.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for funprogram 0.9.2
File Size Uploaded
funprogram-0.9.2.tar.gz 38.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for funprogram 0.9.2
File Interpreter ABI Platform
funprogram-0.9.2-py3-none-any.whl Python 3 none any Details

Total release size: 87.6 kB

Release files / funprogram-0.9.2.tar.gz

Download URL funprogram-0.9.2.tar.gz
Size 38.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1668562f8dc59271c9381f7374da136b8ee73374e22d9b2dbc20c6db35bdfc0a
BLAKE2b-256 checksum
How to use checksums
e9d9d8fabce20fa0a28e3e53c4f208fcb2654c24b2cf4d153577dba0eba68862
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.11.6

Release files / funprogram-0.9.2-py3-none-any.whl

Download URL funprogram-0.9.2-py3-none-any.whl
Size 49.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b367125d5dd1f5f67eea88ade2b2b2accd7ee69331ff8df5251aa5c948ee5367
BLAKE2b-256 checksum
How to use checksums
eb58ea6fc942580f548be6d212a90603d91f9935c2717538c556146ccc514245
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.11.6

Release history Release notifications | RSS feed

This release

0.9.2 This release

2 release files

0.9.1

2 release files

0.9.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page