# Path: pynads/utils/decorators.py
# @optional_kwargs
# def annotate(func, type):
#     """Decorator for adding Haskell style type annotations to a function's
#     docstring on the fly.
#     """
#     old_doc = getattr(func, '__doc__') or ''
#     if old_doc:
#         old_doc = '\n\n{}'.format(old_doc)
# 
#     func.__doc__ = "{!s} :: {!s}{!s}".format(func.__name__,
#                                              type,
#                                              old_doc)
#     return func
# 
# Path: pynads/utils/compat.py
# def wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):
#     """Decorator form of update_wrapper.
# 
#     This is very useful for writing closure based decorators rather than
#     using update_wrapper manually on the closure.
# 
#     Like update_wrapper, this decorator modifies what it's applied it.
#     """
#     return partial(update_wrapper,
#                    wrapped=wrapped,
#                    assigned=assigned,
#                    updated=updated)
# 
# Path: pynads/utils/internal.py
# def _get_name(obj):
#     """Attempts to extract name from a given object.
#     """
#     try:
#         # interop with functools.partial and objects that emulate it
#         if hasattr(obj, 'func') and hasattr(obj.func, '__name__'):
#             return "partialed {!s}".format(obj.func.__name__)
#         # callable object that isn't a function
#         elif not isfunction(obj) and hasattr(obj, '__class__'):
#             return obj.__class__.__name__
#         # must be just a regular function
#         else:
#             return obj.__name__
#     except AttributeError:
#         return ''
# 
# def _get_names(*objs):
#     """Helper function for pynads.funcs.compose that intelligently extracts
#     names from the passed callables, including already composed functions,
#     partially applied functions (functools.partial or similar) and callable
#     objects.
#     """
#     names = []
#     for obj in objs:
#         # extract names from a previously
#         # composed group of functions
#         if hasattr(obj, 'fs'):
#             names.extend(_get_names(*obj.fs))
#         else:
#             names.append(_get_name(obj))
#     return names


from ..utils.decorators import annotate
from ..utils.compat import wraps
from ..utils.internal import _get_name, _get_names
    up the chain. So: compose(f,g,h)(x) is the same as f(g(h(x))).

    If no functions are provided, the identity function is returned. If
    only one function is provided, only that function is returned.

    If the closure is actually generated, it's doc string lists the functions
    it composes.

    >>> def inc(x): return x+1
    >>> def double(x): return x*2
    >>> inc_then_double = compose(dobule, inc)
    >>> inc_then_double(2)
    ... 6
    >>> inc_then_double.__doc__
    ... Composition of: double, inc

    It's also possible to pass partialed callables and callable objects into
    compose as well and it will extract the proper names
    """
    if not fs:
        return identity
    elif len(fs) == 1:
        return fs[0]

    def composed(*a, **k):
        ret = fs[-1](*a, **k)
        for f in fs[-2::-1]:
            ret = f(ret)
        return ret


# Path: pynads/utils/decorators.py
# @optional_kwargs
# def annotate(func, type):
#     """Decorator for adding Haskell style type annotations to a function's
#     docstring on the fly.
#     """
#     old_doc = getattr(func, '__doc__') or ''
#     if old_doc:
#         old_doc = '\n\n{}'.format(old_doc)
# 
#     func.__doc__ = "{!s} :: {!s}{!s}".format(func.__name__,
#                                              type,
#                                              old_doc)
#     return func
# 
# Path: pynads/utils/compat.py
# def wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):
#     """Decorator form of update_wrapper.
# 
#     This is very useful for writing closure based decorators rather than
#     using update_wrapper manually on the closure.
# 
#     Like update_wrapper, this decorator modifies what it's applied it.
#     """
#     return partial(update_wrapper,
#                    wrapped=wrapped,
#                    assigned=assigned,
#                    updated=updated)
# 
# Path: pynads/utils/internal.py
# def _get_name(obj):
#     """Attempts to extract name from a given object.
#     """
#     try:
#         # interop with functools.partial and objects that emulate it
#         if hasattr(obj, 'func') and hasattr(obj.func, '__name__'):
#             return "partialed {!s}".format(obj.func.__name__)
#         # callable object that isn't a function
#         elif not isfunction(obj) and hasattr(obj, '__class__'):
#             return obj.__class__.__name__
#         # must be just a regular function
#         else:
#             return obj.__name__
#     except AttributeError:
#         return ''
# 
# def _get_names(*objs):
#     """Helper function for pynads.funcs.compose that intelligently extracts
#     names from the passed callables, including already composed functions,
#     partially applied functions (functools.partial or similar) and callable
#     objects.
#     """
#     names = []
#     for obj in objs:
#         # extract names from a previously
#         # composed group of functions
#         if hasattr(obj, 'fs'):
#             names.extend(_get_names(*obj.fs))
#         else:
#             names.append(_get_name(obj))
#     return names


# Path: pynads/funcs/pure.py
from ..utils.decorators import annotate
from ..utils.compat import wraps
from ..utils.internal import _get_name, _get_names

    up the chain. So: compose(f,g,h)(x) is the same as f(g(h(x))).

    If no functions are provided, the identity function is returned. If
    only one function is provided, only that function is returned.

    If the closure is actually generated, it's doc string lists the functions
    it composes.

    >>> def inc(x): return x+1
    >>> def double(x): return x*2
    >>> inc_then_double = compose(dobule, inc)
    >>> inc_then_double(2)
    ... 6
    >>> inc_then_double.__doc__
    ... Composition of: double, inc

    It's also possible to pass partialed callables and callable objects into
    compose as well and it will extract the proper names
    """
    if not fs:
        return identity
    elif len(fs) == 1:
        return fs[0]

    def composed(*a, **k):
        ret = fs[-1](*a, **k)
        for f in fs[-2::-1]:
            ret = f(ret)
        return ret


    docstring = "Composition of: {}".format(', '.join(_get_names(*fs)))