# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/10_utils.ipynb.

# %% auto 0
__all__ = ['noop', 'identity', 'safe_not_equal', 'compose']

# %% ../nbs/10_utils.ipynb 3
def noop(*args, **kwargs): return None

# %% ../nbs/10_utils.ipynb 5
def identity(x): return x

# %% ../nbs/10_utils.ipynb 7
def safe_not_equal(a,b):
    "Check if `a` is not equal to `b`"
    primitive = (int, str, bool, frozenset, tuple)
    return (a != b) if isinstance(a, primitive) else True

# %% ../nbs/10_utils.ipynb 9
from functools import reduce
from typing import Callable, TypeVar,  Generic, Union, Optional, Set, Protocol, Any
from typing_extensions import Annotated

# %% ../nbs/10_utils.ipynb 10
def compose( 
    *functions # functions to be composed (left to right)
) -> Callable[[Any], Any]:  # composed function
    """Compose multiple functions left to right.\n
    Examples:\n
        compose()(x) = x 
        compose(f)(x) = f(x) 
        compose(f, g)(x) = g(f(x)) 
        ... 
    """    
    if (len(functions)==0): return lambda x: x # compose()(x) = x
    def pack(x): return x if type(x) is tuple else (x,)
    def call(f, g):
       return lambda *x: g(*pack(f(*pack(x)))) # call in reverse order
    return reduce(call, functions)  # composed function
