pamda
Pamda
Python wrapper for functional programming in object oriented structures.
Inspired heavily by Ramda.
Documentation for Pamda Functions
https://connor-makowski.github.io/pamda/pamda/pamda.html
Key Features
- Simplified functional programming for python
- Core Functions include:
curryarbitrary methods and functionsthunkifyarbitrary methods and functionspipedata iteratively through n functions
- List based path access and features for nested dictionaries
Setup
Make sure you have Python 3.11+ installed on your system. You can download it here.
Installation
pip install pamda
Or with uv:
uv add pamda
Getting Started
Basic Usage
from pamda import pamda
data={'a':{'b':1, 'c':2}}
# Example: Select data given a path and a dictionary
pamda.path(['a','b'])(data) #=> 1
# See documentation for all core pamda functions at
# https://connor-makowski.github.io/pamda/pamda.html
Curry Usage
from pamda import pamda
# Define a function that you want to curry
def myFunction(a,b,c):
return [a,b,c]
# You can call pamda.curry as a function to curry your functions
curriedMyFn=pamda.curry(myFunction)
# Inputs can now be passed in an async fashion
# The function is evaluated when all inputs are added
x=curriedMyFn(1,2)
x(3) #=> [1,2,3]
x(4) #=> [1,2,4]
# Each set of inputs returns a callable function
# You can stack inputs on a single line for clean functional programming
curriedMyFn(1,2)(3) #=> [1,2,3]
For enforcing types, pamda relies on type_enforced but curried objects do not play nice with type_enforced objects. To fix this, there is a special curry function, curryType, that enables type_enforced annotations for your curried functions:
>>> from pamda import pamda
>>>
>>> # Pamda CurryTyped
>>> @pamda.curryTyped
... def add(a:int,b:int):
... return a+b
...
>>> add(1)(1)
2
>>> add(1)(1.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/conmak/development/personal/pamda/pamda/pamda_curry.py", line 43, in __call__
results = self.__fnExecute__(*new_args, **new_kwargs)
File "/home/conmak/development/personal/pamda/venv/lib/python3.10/site-packages/type_enforced/enforcer.py", line 90, in __call__
self.__check_type__(assigned_vars.get(key), value, key)
File "/home/conmak/development/personal/pamda/venv/lib/python3.10/site-packages/type_enforced/enforcer.py", line 112, in __check_type__
self.__exception__(
File "/home/conmak/development/personal/pamda/venv/lib/python3.10/site-packages/type_enforced/enforcer.py", line 34, in __exception__
raise TypeError(f"({self.__fn__.__qualname__}): {message}")
TypeError: (add): Type mismatch for typed variable `b`. Expected one of the following `[<class 'int'>]` but got `<class 'float'>` instead.
Thunkify Usage
from pamda import pamda
# Define a function that you want to thunkify
# thunkify can be called as a function or decorator
@pamda.thunkify
def myFunction(a,b,c):
return [a,b,c]
# The function is now curried and the evaluation is lazy
# This means the function is not evaluated until called
x=myFunction(1,2)
x(3) #=> <pamda.curry_obj object at 0x7fd514e4c820>
x(3)() #=> [1,2,3]
y=x(4)
y() #=> [1,2,4]
Thunkified functions can be executed asynchronously.
from pamda import pamda
import time
@pamda.thunkify
def test(name, wait):
print(f'{name} start')
time.sleep(wait)
print(f'{name} end')
return wait
async_test_a = pamda.asyncRun(test('a',2))
async_test_b = pamda.asyncRun(test('b',1))
async_test_a.asyncWait()
async_test_c = pamda.asyncRun(test('c',1))
The above code would output:
a start
b start
b end
a end
c start
c end
Pipe
from pamda import pamda
def square(x):
return x**2
def half(x):
return x/2
def negate(x):
return -x
# You can pipe data through multiple functions for clean functional programming
pamda.pipe([square, half, negate])(args=(6,),kwargs={}) #=> -18
Use pamda as a subclass
from pamda import pamda
class myClass(pamda):
def myFunction(self, a):
return self.inc(a)
mc=myClass()
mc.myFunction(2) #=> 3
@mc.curry
def addUp(a,b):
return a+b
addUp(1)(2) #=> 3
Pamda Utils
- Pamda also ships with a few helpful utilities
- Check out the documentation here:
1""" 2# Pamda 3[](https://badge.fury.io/py/pamda) 4[](https://opensource.org/licenses/MIT) 5 6Python wrapper for functional programming in object oriented structures. 7 8Inspired heavily by [Ramda](https://ramdajs.com/docs/). 9 10 11## Documentation for Pamda Functions 12https://connor-makowski.github.io/pamda/pamda/pamda.html 13 14## Key Features 15 16- Simplified functional programming for python 17- Core Functions include: 18 - `curry` arbitrary methods and functions 19 - `thunkify` arbitrary methods and functions 20 - `pipe` data iteratively through n functions 21- List based path access and features for nested dictionaries 22 23 24## Setup 25 26Make sure you have Python 3.11+ installed on your system. You can download it [here](https://www.python.org/downloads/). 27 28### Installation 29 30``` 31pip install pamda 32``` 33 34Or with [uv](https://github.com/astral-sh/uv): 35 36``` 37uv add pamda 38``` 39 40## Getting Started 41 42### Basic Usage 43```py 44from pamda import pamda 45 46data={'a':{'b':1, 'c':2}} 47# Example: Select data given a path and a dictionary 48pamda.path(['a','b'])(data) #=> 1 49 50# See documentation for all core pamda functions at 51# https://connor-makowski.github.io/pamda/pamda.html 52``` 53 54### Curry Usage 55```py 56from pamda import pamda 57 58# Define a function that you want to curry 59def myFunction(a,b,c): 60 return [a,b,c] 61 62# You can call pamda.curry as a function to curry your functions 63curriedMyFn=pamda.curry(myFunction) 64 65# Inputs can now be passed in an async fashion 66# The function is evaluated when all inputs are added 67x=curriedMyFn(1,2) 68x(3) #=> [1,2,3] 69x(4) #=> [1,2,4] 70 71# Each set of inputs returns a callable function 72# You can stack inputs on a single line for clean functional programming 73curriedMyFn(1,2)(3) #=> [1,2,3] 74``` 75 76For enforcing types, pamda relies on [type_enforced](https://github.com/connor-makowski/type_enforced) but curried objects do not play nice with `type_enforced` objects. To fix this, there is a special curry function, `curryType`, that enables type_enforced annotations for your curried functions: 77 78```py 79>>> from pamda import pamda 80>>> 81>>> # Pamda CurryTyped 82>>> @pamda.curryTyped 83... def add(a:int,b:int): 84... return a+b 85... 86>>> add(1)(1) 872 88>>> add(1)(1.5) 89Traceback (most recent call last): 90 File "<stdin>", line 1, in <module> 91 File "/home/conmak/development/personal/pamda/pamda/pamda_curry.py", line 43, in __call__ 92 results = self.__fnExecute__(*new_args, **new_kwargs) 93 File "/home/conmak/development/personal/pamda/venv/lib/python3.10/site-packages/type_enforced/enforcer.py", line 90, in __call__ 94 self.__check_type__(assigned_vars.get(key), value, key) 95 File "/home/conmak/development/personal/pamda/venv/lib/python3.10/site-packages/type_enforced/enforcer.py", line 112, in __check_type__ 96 self.__exception__( 97 File "/home/conmak/development/personal/pamda/venv/lib/python3.10/site-packages/type_enforced/enforcer.py", line 34, in __exception__ 98 raise TypeError(f"({self.__fn__.__qualname__}): {message}") 99TypeError: (add): Type mismatch for typed variable `b`. Expected one of the following `[<class 'int'>]` but got `<class 'float'>` instead. 100``` 101 102 103### Thunkify Usage 104```py 105from pamda import pamda 106 107# Define a function that you want to thunkify 108# thunkify can be called as a function or decorator 109@pamda.thunkify 110def myFunction(a,b,c): 111 return [a,b,c] 112 113# The function is now curried and the evaluation is lazy 114# This means the function is not evaluated until called 115x=myFunction(1,2) 116x(3) #=> <pamda.curry_obj object at 0x7fd514e4c820> 117x(3)() #=> [1,2,3] 118 119y=x(4) 120y() #=> [1,2,4] 121``` 122 123Thunkified functions can be executed asynchronously. 124 125```py 126from pamda import pamda 127import time 128 129@pamda.thunkify 130def test(name, wait): 131 print(f'{name} start') 132 time.sleep(wait) 133 print(f'{name} end') 134 return wait 135 136async_test_a = pamda.asyncRun(test('a',2)) 137async_test_b = pamda.asyncRun(test('b',1)) 138async_test_a.asyncWait() 139async_test_c = pamda.asyncRun(test('c',1)) 140``` 141 142The above code would output: 143``` 144a start 145b start 146b end 147a end 148c start 149c end 150``` 151 152### Pipe 153```py 154from pamda import pamda 155 156def square(x): 157 return x**2 158 159def half(x): 160 return x/2 161 162def negate(x): 163 return -x 164 165# You can pipe data through multiple functions for clean functional programming 166pamda.pipe([square, half, negate])(args=(6,),kwargs={}) #=> -18 167``` 168 169### Use pamda as a subclass 170```py 171from pamda import pamda 172 173class myClass(pamda): 174 def myFunction(self, a): 175 return self.inc(a) 176 177mc=myClass() 178mc.myFunction(2) #=> 3 179 180@mc.curry 181def addUp(a,b): 182 return a+b 183 184addUp(1)(2) #=> 3 185``` 186 187## Pamda Utils 188 189- Pamda also ships with a few helpful utilities 190- Check out the documentation here: 191 - https://connor-makowski.github.io/pamda/pamda.html#pamda-utils 192""" 193from .pamda import pamda