Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

minor refactor #961

Merged
203 changes: 203 additions & 0 deletions src/foundation/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
from typing import Callable, Tuple, TypeVar

_T_contra = TypeVar("_T_contra", contravariant=True)
_T2_contra = TypeVar("_T2_contra", contravariant=True)
_T3_contra = TypeVar("_T3_contra", contravariant=True)
_T_co = TypeVar("_T_co", covariant=True)
_T_inv = TypeVar("_T_inv")


def compose(
f: Callable[[_T_inv], _T_co], g: Callable[[_T_contra], _T_inv]
) -> Callable[[_T_contra], _T_co]:
"""
`f after g` composition of functions

Equiv: lamdba x -> f(g(x))
"""

def inter_(value: _T_contra) -> _T_co:
return f(g(value))

return inter_


def identity(value: _T_inv) -> _T_inv:
"""
identity function
"""
return value


def constant(value: _T_inv) -> Callable[[_T_contra], _T_inv]:
"""
constant function
"""

def _intern(_: _T_contra) -> _T_inv:
return value

return _intern


def pipe(
f: Callable[[_T_contra], _T_inv], g: Callable[[_T_inv], _T_co]
) -> Callable[[_T_contra], _T_co]:
"""
`g after f` composition of functions (reverse of compose)
"""
return compose(g, f)


def apply_reverse(input: _T_contra) -> Callable[[Callable[[_T_contra], _T_co]], _T_co]:
"""
Applying a function. Equiv curried `(&)` in Haskel
a -> (a -> b) -> b

Example usage:

>>> def _mul(a: int, b: int) -> int:
... return a * b
>>> def _add(a: int, b: int) -> int:
... return a + b
>>> list(map(apply_reverse(3), [curry2(_mul)(2), curry2(_add)(5)])) == [6, 8]
True

"""

def _intern(func: Callable[[_T_contra], _T_co]) -> _T_co:
return func(input)

return _intern


def curry2(
func: Callable[[_T_contra, _T2_contra], _T_co],
) -> Callable[[_T_contra], Callable[[_T2_contra], _T_co]]:
"""
Transforms 2-param function into two nested 1-param functions

>>> def _mul(a: int, b: int) -> int:
... return a * b
>>> _mul(2, 3) == 6
True
>>> curry2(_mul)(2)(3) == 6
True

"""

def _intern(input: _T_contra) -> Callable[[_T2_contra], _T_co]:
def _intern2(input2: _T2_contra) -> _T_co:
return func(input, input2)

return _intern2

return _intern


def uncurry2(
func: Callable[[_T_contra], Callable[[_T2_contra], _T_co]],
) -> Callable[[_T_contra, _T2_contra], _T_co]:
"""
Transforms two nested 1-param functions into one 2-param function

>>> def _mul_c(a: int):
... def _intern(b: int) -> int:
... return a * b
...
... return _intern
>>> _mul_c(2)(3) == 6
True
>>> uncurry2(_mul_c)(2, 3) == 6
True

"""

def _intern(input: _T_contra, input2: _T2_contra) -> _T_co:
return func(input)(input2)

return _intern


def curry3(
func: Callable[[_T_contra, _T2_contra, _T3_contra], _T_co],
) -> Callable[[_T_contra], Callable[[_T2_contra], Callable[[_T3_contra], _T_co]]]:
def _intern(input: _T_contra) -> Callable[[_T2_contra], Callable[[_T3_contra], _T_co]]:
def _intern2(input2: _T2_contra) -> Callable[[_T3_contra], _T_co]:
def _intern3(input3: _T3_contra) -> _T_co:
return func(input, input2, input3)

return _intern3

return _intern2

return _intern


def fst(t: Tuple[_T_inv, _T2_contra]) -> _T_inv:
"""get first of tuple
>>> fst((1, 2)) == 1
True
"""
return t[0]


def snd(t: Tuple[_T_contra, _T_inv]) -> _T_inv:
"""get second of tuple
>>> snd((1, 2)) == 2
True
"""
return t[1]


def flip(
func: Callable[[_T_contra, _T2_contra], _T_co],
) -> Callable[[_T2_contra, _T_contra], _T_co]:
"""flips params
>>> def minus(a: int, b: int) -> int:
... return a - b
>>> minus(5, 3) == 2
True
>>> flip(minus)(5, 3) == -2
True
"""

def _intern(input2: _T2_contra, input: _T_contra) -> _T_co:
return func(input, input2)

return _intern


def compact2(
func: Callable[[_T_contra, _T2_contra], _T_co],
) -> Callable[[Tuple[_T_contra, _T2_contra]], _T_co]:
"""Compacts two parameters into one tuple"""

def _intern(input: Tuple[_T_contra, _T2_contra]) -> _T_co:
return func(fst(input), snd(input))

return _intern


def expand2(
func: Callable[[Tuple[_T_contra, _T2_contra]], _T_co],
) -> Callable[[_T_contra, _T2_contra], _T_co]:
def _intern(input: _T_contra, input2: _T2_contra) -> _T_co:
return func((input, input2))

return _intern


def pipe2(
f: Callable[[_T_contra, _T2_contra], _T_inv], g: Callable[[_T_inv], _T_co]
) -> Callable[[_T_contra, _T2_contra], _T_co]:
"""
`g after f` composition of functions (reverse of compose). f takes 2 params
>>> def mul(a: int, b: int) -> int:
... return a * b
>>> def add5(a: int) -> int:
... return a + 5
>>> pipe2(mul, add5)(2, 3) == 11
True
"""
return expand2(pipe(compact2(f), g))
101 changes: 101 additions & 0 deletions src/foundation/core/optional/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from typing import Callable, Optional, TypeVar

_Tin = TypeVar("_Tin")
_Tin2 = TypeVar("_Tin2")
_Tin3 = TypeVar("_Tin3")
_Tout = TypeVar("_Tout")


def bind(
func: Callable[[_Tin], Optional[_Tout]],
) -> Callable[[Optional[_Tin]], Optional[_Tout]]:
"""
Monadic bind for Optional.

Example usage:
>>> def div8(divider: int) -> Optional[float]:
... if divider == 0:
... return None
... return 8 / divider
>>> b = bind(div8)

Basic operation on div8:
>>> div8(2) == 4
True
>>> div8(0) == None
True

Passing None throws:
>>> div8(None)
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for /: 'int' and 'NoneType'

Binding works for basic and None:
>>> b(2) == 4
True
>>> b(0) == None
True
>>> b(None) == None
True

"""

def _intern(input: Optional[_Tin]) -> Optional[_Tout]:
if input:
return func(input)
return None

return _intern


def lift2(
func: Callable[[_Tin, _Tin2], _Tout],
) -> Callable[[Optional[_Tin], Optional[_Tin2]], Optional[_Tout]]:
"""
Lifts regular function into Optional. (Lift2 for Optional Applicative Functor)
(a -> b -> c) -> Maybe a -> Maybe b -> Maybe c

Example usage:
>>> def _mul(input: int, input2: int) -> int:
... return input * input2
>>> l = lift2(_mul)

Works for numbers:
>>> l(2, 3) == 6
True

Works for None in any and/or all parameter:
>>> l(2, None) == None
True
>>> l(None, 3) == None
True
>>> l(None, None) == None
True

"""

def _intern(input: Optional[_Tin], input2: Optional[_Tin2]) -> Optional[_Tout]:
if input and input2:
return func(input, input2)
return None

return _intern


def lift3(
func: Callable[[_Tin, _Tin2, _Tin3], _Tout],
) -> Callable[[Optional[_Tin], Optional[_Tin2], Optional[_Tin3]], Optional[_Tout]]:
"""
Lifts regular function into Optional. see lift2 for Optional Applicative Functor
(a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
"""

def _intern(
input: Optional[_Tin], input2: Optional[_Tin2], input3: Optional[_Tin3]
) -> Optional[_Tout]:
if input and input2 and input3:
return func(input, input2, input3)
return None

return _intern
Loading
Loading