przemyslawjanpietrzak / pyMonet

Licence: MIT license
High abstract python library for functional programming. Contains algebraic data structures known (or unknown) from Haskell or Scala.

Programming Languages

python
139335 projects - #7 most used programming language
Makefile
30231 projects

Projects that are alternatives of or similar to pyMonet

Kea
Composable Functional Programming in R
Stars: ✭ 18 (-41.94%)
Mutual labels:  monads
Fpgo
Monad, Functional Programming features for Golang
Stars: ✭ 165 (+432.26%)
Mutual labels:  monads
Pratica
🥃 Functional Algebraic Data Types
Stars: ✭ 246 (+693.55%)
Mutual labels:  monads
Purefun
Functional Programming library for Java
Stars: ✭ 37 (+19.35%)
Mutual labels:  monads
Pipetools
Functional plumbing for Python
Stars: ✭ 143 (+361.29%)
Mutual labels:  monads
Maryamyriameliamurphies.js
A library of Haskell-style morphisms ported to ES2015 JavaScript using Babel.
Stars: ✭ 177 (+470.97%)
Mutual labels:  monads
Oslash
Functors, Applicatives, And Monads in Python
Stars: ✭ 561 (+1709.68%)
Mutual labels:  monads
freestyle-kafka
Freestyle Kafka
Stars: ✭ 17 (-45.16%)
Mutual labels:  monads
Swift Monad Maybe Reader And Try
Proof of concept: Maybe, Reader and Try monad
Stars: ✭ 159 (+412.9%)
Mutual labels:  monads
Neither
Either and Maybe monads for better error-handling in C++ ↔️
Stars: ✭ 236 (+661.29%)
Mutual labels:  monads
Fear
Ruby port of some Scala's monads
Stars: ✭ 58 (+87.1%)
Mutual labels:  monads
Datum
pure functional and generic programming for Erlang
Stars: ✭ 111 (+258.06%)
Mutual labels:  monads
Mu Haskell
Mu (μ) is a purely functional framework for building micro services.
Stars: ✭ 215 (+593.55%)
Mutual labels:  monads
Monad Skeleton
Operational monad library
Stars: ✭ 30 (-3.23%)
Mutual labels:  monads
kudojs
A utility library to write code in functional programming style in Javascript
Stars: ✭ 22 (-29.03%)
Mutual labels:  monads
Freestyle
A cohesive & pragmatic framework of FP centric Scala libraries
Stars: ✭ 627 (+1922.58%)
Mutual labels:  monads
Scala Workflow
Boilerplate-free syntax for computations with effects
Stars: ✭ 173 (+458.06%)
Mutual labels:  monads
hawkweed
Yet another implementation of missing functions for Python
Stars: ✭ 20 (-35.48%)
Mutual labels:  monads
konad
Monads composition API that just works. For OOP developers
Stars: ✭ 62 (+100%)
Mutual labels:  monads
Monads
👻 Type safe Option, Result, and Either types; inspired by Rust
Stars: ✭ 228 (+635.48%)
Mutual labels:  monads

pyMonet

Build Status Coverage Status Documentation Status PyPI version License: MIT

High abstract python library for functional programming. Contains algebraic data structures known (or unknown) from Haskell or Scala. With MIT licence. Docs

Install:

pip install pymonet

Content:

Either

The Either type represents values with two possibilities: B value of type Either<A, B> is either Left or Right. But not both in the same time.

Maybe

Maybe type is the most common way of representing nothingness (or the null type). Maybe is effectively abstract and has two concrete subtypes: Box (also Some) and Nothing.

Box

Boxs are data-types that store values. No restriction is placed on how they store these values, though there may be restrictions on some methods if a Box is also an instance of a sub-class of Box.

Semigroups

In mathematics, a semigroup is an algebraic structure consisting of a set together with an associative binary operation. A semigroup generalizes a monoid in that there might not exist an identity element. It also (originally) generalized a group (a monoid with all inverses) to a type where every element did not have to have an inverse, this the name semigroup.

Lazy

Lazy are data-types that store functions. Stored function will not be called until call of bind method

ImmutableList

Implementation of list data structures with immutable methods

Task

Task are data-type for handle execution of functions (in lazy way) transform results of this function and handle errors.

Try

The Try control gives us the ability write safe code without focusing on try-catch blocks in the presence of exceptions.

Validation

It that can hold either a success value or a failure value and has methods for accumulating errors.

Utils

Set of functional programming helpers

Either

The Either type represents values with two possibilities: B value of type Either<A, B> is either Left or Right. But not both in the same time. Left represents error value so any maps and bind will NOT be applied on it.

from pymonet.either import Left, Right
from pymonet.utils import identity

def divide(divided, divider):
    if divider == 0:
        return Left('can not divide by 0')
    return Right(divided, divider)

def handle_error(value):
    print ('error {}'.format(value))

def handle_success(value):
    print ('success {}'.format(value))

(divide(42, 0)
    .map(lambda value: value + 1)
    .bind(lambda value: Right(value + 1))
    .case(error=handle_error, success=handle_success))
# error 42

(divide(42, 1)
    .map(identity, lambda value: value + 1)
    .bind(lambda value: Right(value + 1))
    .case(error=handle_error, success=handle_success))
# success  44

Maybe

Maybe type is the most common way of representing nothingness (or the null type) with making the possibilities of NullPointer issues disappear. Maybe is effectively abstract and has two concrete subtypes: Some (also Box) and None (also Nothing).

from pymonet.Maybe import Maybe


def get_index(item):
    if item in [1,2,3]:
        return Maybe.just(42)
    return Maybe.nothing()

get_index(42).get_or_else(0)  # 0
get_index(1).get_or_else(0)  # 3

bind and map methods will be applied only when maybe is not empty

from pymonet.Maybe import Maybe


get_index(42)\
  .map(lambda value: value + 1)\
  .bind(lambda value: Maybe.just(value + 1))\
  .get_or_else(0)
# 0

get_index(1)\
  .map(lambda value: value + 1)\
  .bind(lambda value: Maybe.just(value + 1))\
  .get_or_else(0)
# 3

Filter method will be applied on maybe value and return it with or without value, depend on filter result:

from pymonet.Maybe import Maybe


get_index(42)\
    .filter(lambda value: value % 2 == 0)\
    .get_or_else(0)
# 0

get_index(3)\
    .filter(lambda value: value % 2 == 0)\
    .get_or_else(0)
# 0

get_index(2)\
    .filter(lambda value: value % 2 == 0)\
    .get_or_else(0)
# 2

Box

Boxs are data-types that store values. No restriction is placed on how they store these values, though there may be restrictions on some methods if a Box is also an instance of a sub-class of Box.

from pymonet.box import Box
box = Box(42)  # Box<42>
(box
    .map(lambda value: value + 1)  # Box<43>
    .map(lambda value: str(value))  # Box<"43">
    .map(lambda value: value[::-1])  # Box<"34">
    .bind(lambda value: "output = " + value))  # "output = 34"

Semigroups

In mathematics, a semigroup is an algebraic structure consisting of a set together with an associative binary operation. A semigroup generalizes a monoid in that there might not exist an identity element. It also (originally) generalized a group (a monoid with all inverses) to a type where every element did not have to have an inverse, this the name semigroup.

All

from pymonet.semigroups import All

All(True).concat(All(False))  # All<False>
All(True).concat(All(True))  # All<True>

== operator compares value of semigroups

All(True) == All(True)  # True
All(True) == All(False)  # False

Sum

from pymonet.semigroups import Sum

Sum(42).concat(Sum(1))  # Sum<43>
Sum(42).concat(Sum(1)).concat(Sum(1))  # Sum<44>
Sum(42).concat(Sum(1).concat(Sum(1)))  # Sum<44>

Sum(42).bind(lambda value: value)  # 42

First

from pymonet.semigroups import First

First('first').concat(First('Second'))  # First<"first">
First('first').bind(lambda value: value[::-1])  # "tsrif"

Map

from pymonet.semigroups import Sum, All, First, Map
ingredient1 = Map({'score': Sum(1), 'won': All(True), 'captain': First('captain america')})
ingredient2 = Map({'score': Sum(2), 'won': All(True), 'captain': First('iron man')})
ingredient1.concat(ingredient2)  # Map<{'score': Sum(3), 'won': All(True), 'captain': First('captain america')}>

Lazy

Lazy are data-types that store functions. Stored function will not be called until call of bind method

from pymonet.lazy import Lazy

def fn():
    print('fn call')
    return 42

def mapper(value):
    print('mapper side effect of ' + value)
    return value + 1

def side_effect(value):
    print('side effect of ' + value)

lazy = Lazy(fn)
mapped_lazy = lazy.map(mapper)
mapped_lazy.bind(side_effect)  
# fn call
# mapper side effect of 42
# side effect of 42

Lazy instances memoize output of constructor function

lazy = Lazy(fn)
value1 = lazy.get()
# fn call
value2 = lazy.get()
print(value1, value2)
# 42, 42

ImmutableList

Implementation of list data structures with immutable methods

lst = ImmutableList.of(1, 2, 3)

lst.map(increase) # ImmutableList.of(2, 3, 4)
lst.filter(lambda item: item % 2 == 0) # ImmutableList.of(2)
lst.find(lambda item: item % 2 == 0) # 2
lst.map(increase) # ImmutableList.of(2, 3, 4)
lst.reduce(lambda acc, curr: acc + curr, 0) # 6

Task

Task are data-type for handle execution of functions (in lazy way) transform results of this function and handle errors.

from pymonet.task import Task

def resolvable_fn(reject, resolve):
    print('resolve side effect')
    resolve(42)

def rejectable_fn(reject, resolve):
    print('reject side effect')
    reject(0)

resolvable_task = Task.of(resolvable_fn)
rejectable_task = Task.of(rejectable_fn)

map method will be applied only on resolvable tasks during calling bind method

resolvable_task.map(lambda value: value + 1)  # Task<() -> 43>
rejectable_task.map(lambda value: value + 1)  # Task<() -> 0>

bind method will be applied only on resolvable tasks. bind also will call stored function

def mapper(value):
    print('mapper side effect ' + value)
    return value + 1

resolvable_task.bind(mapper)
# resolve side effect
# mapper side effect 42

rejectable_task.bind(mapper)
# reject side effect

Try

The Try control gives us the ability write safe code without focusing on try-catch blocks in the presence of exceptions.

from pymonet.monad_try import Try

def divide(dividend, divisor):
    return dividend / divisor

def success_callback(value):
    print('success: {}'.format(value))

def fail_callback(error):
    print('error: {}'.format(value))

(Try.of(divide, 42, 2)
    .on_success(success_callback)
    .on_fail(fail_callback))
# success: 21

(Try.of(divide, 42, 0)
    .on_success(success_callback)
    .on_fail(fail_callback))
#error: division by zero

map method will be only applied mapper when exception was not thrown

(Try.of(divide, 42, 2)
    .map(lambda value: value + 1)
    .on_success(success_callback)
    .on_fail(fail_callback))
# success: 22

(Try.of(divide, 42, 0)
    .on_success(success_callback)
    .map(lambda value: value + 1)
    .on_fail(fail_callback))
#error: division by zero

get_or_else method returns value when exception was not thrown

Try.of(divide, 42, 2).get_or_else('Holy Grail') # 21
Try.of(divide, 42, 0).get_or_else('Holy Grail') # 'Holy Grail'

get method should return value with or without exception thrown

Try.of(divide, 42, 2).get()  # 21
Try.of(divide, 42, 0).get()  # ZeroDivisionError<'division by zero'>

Validation

It that can hold either a success value or a failure value and has methods for accumulating errors

from pymonet.validation import Validation


def test_validation_is_fail():
    assert Validation.fail(['fail']).is_fail()


def validate_length(value):
    if len(value) < 5:
        return Validation.fail(['value not long enough'])
    return Validation.success()


def validate_uppercase(value):
    if value[0].upper() != value[0]:
        return Validation.fail(['value not uppercase'])
    return Validation.success()


def validate_contains_special_character(value):
    if re.match(r'^[a-zA-Z0-9_]*$', value):
        return Validation.fail(['value not contains special character'])
    return Validation.success()


def validate(value):
    return (Validation.success(value)
            .ap(validate_length)
            .ap(validate_uppercase)
            .ap(validate_contains_special_character))


validate('Success$') # Validation['Success$', []]
validate('Success') # Validation['Success$', ['value not uppercase']]
validate('S$') # Validation['Success$', ['value not long enough']]
validate('s$') # Validation['Success$', ['value not long enough', 'value not uppercase']]
validate('s') # Validation['Success$', ['value not long enough', 'value not uppercase', 'value not contains special character']]

Utils

compose

Compose: performs right-to-left function composition.

from pymonet.utils import \
    increase,\
    compose,\
    curried_map as map,\
    curried_filter as filter

compose(
    list(range(10)),
    map(increase),
    filter(is_odd)
)
#[1, 3, 5, 7, 9]

pipe

Pipe: performs left-to-right function composition.

from pymonet.utils import increase, pipe

pipe(42, increase, lambda value: value * 2)
#86

cond

Returns a function which encapsulates if/else, if/else, ... logic. cond takes a list of (predicate, transformer) pairs. All of the arguments to fn are applied to each of the predicates in turn until one returns a truthy value, at which point fn returns the result of applying its arguments to the corresponding transformer.

from pymonet.utils import cond

fn = cond([
    (lambda arg: arg == 0, lambda: 'first'),
    (lambda arg: arg == 1, lambda: 'second'),
    (lambda arg: arg == 2, lambda: 'third').
])
fn(1) #  second
# lambda arg: arg == 2 will not be call

memoize

Creates a new function that, when invoked, caches the result of calling fn for a given argument set and returns the result. Subsequent calls to the memoized fn with the same argument set will not result in an additional call to fn; instead, the cached result for that set of arguments will be returned.

from pymonet.utils import memoize, eq

def fn(arg):
    print('fn flag')
    return arg + 1

memoized_fn = memoize(fn)
memoized_fn(42) # 43
# fn flag

memoized_fn(42) # 43
# print to called

memoized_fn(43) # 44
# fn flag
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].