Apply a function of a potentially nested list of lists.

    Parameters
    ----------
    fn : callable
        The function to apply to each element (and sub elements) in iterable
    iterable : iterable
        An iterable, sequence, sequence of sequences etc. :code:`fn` will be
        applied to each element in each list.
    output_type : callable, optional
        if None, a map with sub-maps in the same structure as :code:`iterable`
        will be returned, otherwise the callable will be applied to each
        sequence (i.e. :code:`list` will return lists of lists etc).

    Returns
    -------
    map or iterable type :
        if :code:`output_type` is None, a map with sub-maps in the same
        structure as :code:`iterable` will be returned, otherwise the callable
        will be applied to each sequence (i.e. :code:`list` will return lists
        of lists etc).

    Examples
    --------
    >>> seq = [1, 2, [3, 4, [5, 6]], 7]
    >>> map_recursive(lambda x: x > 4, seq, output_type=list)
    [False, False, [False, False, [True, True]], True]

    >>> map_recursive(lambda x: 2 * x, seq, output_type=tuple)
    (2, 4, (6, 8, (10, 12)), 14)

def map_recursive(fn, iterable, output_type=None):
    """
    Apply a function of a potentially nested list of lists.

    Parameters
    ----------
    fn : callable
        The function to apply to each element (and sub elements) in iterable
    iterable : iterable
        An iterable, sequence, sequence of sequences etc. :code:`fn` will be
        applied to each element in each list.
    output_type : callable, optional
        if None, a map with sub-maps in the same structure as :code:`iterable`
        will be returned, otherwise the callable will be applied to each
        sequence (i.e. :code:`list` will return lists of lists etc).

    Returns
    -------
    map or iterable type :
        if :code:`output_type` is None, a map with sub-maps in the same
        structure as :code:`iterable` will be returned, otherwise the callable
        will be applied to each sequence (i.e. :code:`list` will return lists
        of lists etc).

    Examples
    --------
    >>> seq = [1, 2, [3, 4, [5, 6]], 7]
    >>> map_recursive(lambda x: x > 4, seq, output_type=list)
    [False, False, [False, False, [True, True]], True]

    >>> map_recursive(lambda x: 2 * x, seq, output_type=tuple)
    (2, 4, (6, 8, (10, 12)), 14)
    """
    def applyormap(it):
        if issequence(it):
            return map_recursive(fn, it, output_type)
        else:
            return fn(it)

    applied = map(applyormap, iterable)
    return output_type(applied) if output_type else applied