pamda

Pamda

PyPI version License: MIT

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:
    • curry arbitrary methods and functions
    • thunkify arbitrary methods and functions
    • pipe data 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

  1"""
  2# Pamda
  3[![PyPI version](https://badge.fury.io/py/pamda.svg)](https://badge.fury.io/py/pamda)
  4[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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