docs: def merge(a, b, op=None, recurse_list=False, max_depth=None):
      """Immutable merge ``a`` structure with ``b`` using binary operator ``op``
      on leaf nodes. All nodes at, or below, ``max_depth`` are considered to be
      leaf nodes.
  
      Merged structure is returned, input data structures are not modified.
  
      If ``recurse_list=True``, leaf lists of equal length will be merged on a
      list-element level. Lists are considered to be leaf nodes by default
      (``recurse_list=False``), and they are merged with user-provided ``op``.
      Note the difference::
  
          merge([1, 2], [3, 4]) ==> [1, 2, 3, 4]
  
          merge([1, 2], [3, 4], recurse_list=True) ==> [4, 6]
  
      """
  
      if op is None:
          op = operator.add
  
      if max_depth is not None:
          if max_depth < 1:
              return op(a, b)
          else:
              max_depth -= 1
  
      if isinstance(a, dict) and isinstance(b, dict):
          result = {}
          for key in set(chain(a.keys(), b.keys())):
              if key in a and key in b:
                  result[key] = merge(a[key], b[key],
                                      op=op, recurse_list=recurse_list,
                                      max_depth=max_depth)
              elif key in a:
                  result[key] = deepcopy(a[key])
              elif key in b:
                  result[key] = deepcopy(b[key])
          return result
  
      elif isinstance(a, list) and isinstance(b, list):
          if recurse_list and len(a) == len(b):
              # merge subelements
              result = []
              for idx in range(len(a)):
                  result.append(merge(a[idx], b[idx],
                                      op=op, recurse_list=recurse_list,
                                      max_depth=max_depth))
              return result
          else:
              # merge lists
              return op(a, b)
  
      # all other merge ops should be handled by ``op``.
      # default ``operator.add`` will handle addition of numeric types, but fail
      # with TypeError for incompatible types (eg. str + None, etc.)
      return op(a, b)