def _single_merge(a, b, error_path=None, overwrite=False,
                  append=False, list_of_dicts=False):
    """merges b into a
    """
    if error_path is None:
        error_path = []

    if list_of_dicts and is_list_of_dict_like(a) and is_list_of_dict_like(b):
        if len(a) != len(b):
            raise ValueError(
                'list of dicts are of different lengths at '
                '"{0}": old: {1}, new: {2}'.format('.'.join(error_path), a, b))
        return [_single_merge(a_item, b_item,
                              error_path + ["iter_{}".format(i)],
                              overwrite, append, list_of_dicts)
                for i, (a_item, b_item) in enumerate(zip(a, b))]

    for key in b:
        if key in a:
            if is_dict_like(a[key]) and is_dict_like(b[key]):
                _single_merge(a[key], b[key], error_path +
                              [str(key)], overwrite, append, list_of_dicts)
            elif (isinstance(a[key], list)
                  and isinstance(b[key], list) and append):
                a[key] += b[key]
            elif (list_of_dicts
                  and is_list_of_dict_like(a[key])
                  and is_list_of_dict_like(b[key])):
                if len(a[key]) != len(b[key]):
                    raise ValueError(
                        'list of dicts are of different lengths at '
                        '"{0}": old: {1}, new: {2}'.format(
                            '.'.join(error_path + [str(key)]), a[key], b[key]))
                for i, (a_item, b_item) in enumerate(zip(a[key], b[key])):
                    _single_merge(a_item, b_item,
                                  error_path + [str(key), "iter_{}".format(i)],
                                  overwrite, append, list_of_dicts)
            elif a[key] == b[key]:
                pass  # same leaf value
            elif overwrite:
                a[key] = b[key]
            else:
                raise ValueError(
                    'different data already exists at '
                    '"{0}": old: {1}, new: {2}'.format(
                        '.'.join(error_path + [str(key)]), a[key], b[key]))
        else:
            a[key] = b[key]
    return a