def apply_to_collection(data, fn):
    """
    Applies a given function recursively to a tuple, list or dictionary of
    things with arbitrary nesting (including none). Returns the new collection.
    """
    if isinstance(data, (tuple, list)):
        return type(data)(apply_to_collection(item, fn) for item in data)
    elif isinstance(data, dict):
        return type(data)((k, apply_to_collection(v, fn))
                          for k, v in data.items())
    else:
        return fn(data)